diff --git a/.circleci/bootstrap.sh b/.circleci/bootstrap.sh index 978464efe..30dc0b316 100755 --- a/.circleci/bootstrap.sh +++ b/.circleci/bootstrap.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright 2016 The Kubernetes Authors All rights reserved. +# 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. diff --git a/.circleci/deploy.sh b/.circleci/deploy.sh index db4a1d3a2..f70cba21b 100755 --- a/.circleci/deploy.sh +++ b/.circleci/deploy.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright 2016 The Kubernetes Authors All rights reserved. +# 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. diff --git a/.circleci/test.sh b/.circleci/test.sh index 249873b99..31c69deba 100755 --- a/.circleci/test.sh +++ b/.circleci/test.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright 2016 The Kubernetes Authors All rights reserved. +# 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. diff --git a/Gopkg.lock b/Gopkg.lock index 3ef21b868..7bef996b8 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -2,113 +2,154 @@ [[projects]] + digest = "1:aa65b4877ac225076b4362885e9122fdf6a8728f735749c24f1aeabcad9bdaba" name = "cloud.google.com/go" packages = [ "compute/metadata", - "internal" + "internal", ] + pruneopts = "UT" revision = "3b1ae45394a234c385be014e9a488f2bb6eef821" [[projects]] + digest = "1:42438de56663c9af79baacdcb986156b1820e0d2f030458040055c25d5c9ae01" name = "github.com/Azure/go-ansiterm" packages = [ ".", - "winterm" + "winterm", ] + pruneopts = "UT" revision = "19f72df4d05d31cbe1c56bfc8045c96babff6c7e" [[projects]] + digest = "1:c54c12f9d6716aad8079084272c0c6ac6f21abe203c374e13b512e05d51669fb" name = "github.com/Azure/go-autorest" packages = [ "autorest", "autorest/adal", "autorest/azure", - "autorest/date" + "autorest/date", ] - revision = "d4e6b95c12a08b4de2d48b45d5b4d594e5d32fab" - version = "v9.9.0" + pruneopts = "UT" + revision = "1ff28809256a84bb6966640ff3d0371af82ccba4" [[projects]] + digest = "1:b16fbfbcc20645cb419f78325bb2e85ec729b338e996a228124d68931a6f2a37" name = "github.com/BurntSushi/toml" packages = ["."] + pruneopts = "UT" revision = "b26d9c308763d68093482582cea63d69be07a0f0" version = "v0.3.0" [[projects]] + digest = "1:51d5156c2de01719fdf90b21197b95bc7e8c9d43ca0d5c3f5c875b8b530077c8" name = "github.com/MakeNowJust/heredoc" packages = ["."] + pruneopts = "UT" revision = "bb23615498cded5e105af4ce27de75b089cbe851" [[projects]] + digest = "1:6e6779c1e7984081358a4aee6f944233c8cbabfb28ca9dc0e20af595d476ebf4" name = "github.com/Masterminds/semver" packages = ["."] + pruneopts = "UT" revision = "517734cc7d6470c0d07130e40fd40bdeb9bcd3fd" version = "v1.3.1" [[projects]] + digest = "1:46a054a232ea2b7f0a35398d682b433d26ba9975fce9197b1784824402059f5b" name = "github.com/Masterminds/sprig" packages = ["."] + pruneopts = "UT" revision = "6b2a58267f6a8b1dc8e2eb5519b984008fa85e8c" version = "v2.15.0" [[projects]] + digest = "1:035b152b3f30c1d32a82862fd7af2da1894514d748b0ff7c435ed48e75a0b58a" name = "github.com/Masterminds/vcs" packages = ["."] + pruneopts = "UT" revision = "3084677c2c188840777bff30054f2b553729d329" version = "v1.11.1" [[projects]] + digest = "1:792c6f8317411834d22db5be14276cd87d589cb0f8dcc51c042f0dddf67d60b1" name = "github.com/PuerkitoBio/purell" packages = ["."] + pruneopts = "UT" revision = "8a290539e2e8629dbc4e6bad948158f790ec31f4" version = "v1.0.0" [[projects]] + digest = "1:61e5d7b1fabd5b6734b2595912944dbd9f6e0eaa4adef25e5cbf98754fc91df1" name = "github.com/PuerkitoBio/urlesc" packages = ["."] + pruneopts = "UT" revision = "5bd2802263f21d8788851d5305584c82a5c75d7e" [[projects]] + digest = "1:10bb36acbe3beb4e529f2711e02c66335adc17dffaceb1d6ceca9554c2d8baa0" name = "github.com/aokoli/goutils" packages = ["."] + pruneopts = "UT" revision = "9c37978a95bd5c709a15883b6242714ea6709e64" [[projects]] + digest = "1:311ccee815cbad2b98ab1f1f3f666db6f7f9d5e425cfd99197f6e925d3907848" name = "github.com/asaskevich/govalidator" packages = ["."] + pruneopts = "UT" revision = "7664702784775e51966f0885f5cd27435916517b" version = "v4" [[projects]] - name = "github.com/beorn7/perks" - packages = ["quantile"] - revision = "3ac7bf7a47d159a033b107610db8a1b6575507a4" + branch = "master" + digest = "1:95e08278c876d185ba67533f045e9e63b3c9d02cbd60beb0f4dbaa2344a13ac2" + name = "github.com/chai2010/gettext-go" + packages = [ + "gettext", + "gettext/mo", + "gettext/plural", + "gettext/po", + ] + pruneopts = "UT" + revision = "bf70f2a70fb1b1f36d90d671a72795984eab0fcb" [[projects]] + digest = "1:cc832d4c674b57b5c67f683f75fba043dd3eec6fcd9b936f00cc8ddf439f2131" name = "github.com/cpuguy83/go-md2man" packages = ["md2man"] + pruneopts = "UT" revision = "71acacd42f85e5e82f70a55327789582a5200a90" version = "v1.0.4" [[projects]] + digest = "1:6b21090f60571b20b3ddc2c8e48547dffcf409498ed6002c2cada023725ed377" name = "github.com/davecgh/go-spew" packages = ["spew"] + pruneopts = "UT" revision = "782f4967f2dc4564575ca782fe2d04090b5faca8" [[projects]] + digest = "1:76dc72490af7174349349838f2fe118996381b31ea83243812a97e5a0fd5ed55" name = "github.com/dgrijalva/jwt-go" packages = ["."] - revision = "01aeca54ebda6e0fbfafd0a524d234159c05ec20" + pruneopts = "UT" + revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e" + version = "v3.2.0" [[projects]] + digest = "1:4189ee6a3844f555124d9d2656fe7af02fca961c2a9bad9074789df13a0c62e0" name = "github.com/docker/distribution" packages = [ "digestset", - "reference" + "reference", ] + pruneopts = "UT" revision = "edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c" [[projects]] + digest = "1:5eebe80a8c7778ba2ac8d0ce0debce3068d10a1e70891c2294d3521ae23865fc" name = "github.com/docker/docker" packages = [ "api/types", @@ -123,71 +164,95 @@ "api/types/swarm/runtime", "api/types/versions", "pkg/term", - "pkg/term/windows" + "pkg/term/windows", ] + pruneopts = "UT" revision = "4f3616fb1c112e206b88cb7a9922bf49067a7756" [[projects]] + digest = "1:87dcb59127512b84097086504c16595cf8fef35b9e0bfca565dfc06e198158d7" name = "github.com/docker/go-connections" packages = ["nat"] + pruneopts = "UT" revision = "3ede32e2033de7505e6500d6c868c2b9ed9f169d" version = "v0.3.0" [[projects]] + digest = "1:57d39983d01980c1317c2c5c6dd4b5b0c4a804ad2df800f2f6cbcd6a6d05f6ca" name = "github.com/docker/go-units" packages = ["."] + pruneopts = "UT" revision = "9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1" [[projects]] + digest = "1:58be7025fd84632dfbb8a398f931b5bdbbecc0390e4385df4ae56775487a0f87" name = "github.com/docker/spdystream" packages = [ ".", - "spdy" + "spdy", ] + pruneopts = "UT" revision = "449fdfce4d962303d702fec724ef0ad181c92528" [[projects]] + digest = "1:11e94345a96c7ffd792e2c6019b79fd9c51d9faf55201d23a96c38dc09533a01" name = "github.com/evanphx/json-patch" packages = ["."] + pruneopts = "UT" revision = "944e07253867aacae43c04b2e6a239005443f33a" [[projects]] branch = "master" + digest = "1:5e0da1aba1a7b125f46e6ddca43e98b40cf6eaea3322b016c331cf6afe53c30a" name = "github.com/exponent-io/jsonpath" packages = ["."] + pruneopts = "UT" revision = "d6023ce2651d8eafb5c75bb0c7167536102ec9f5" [[projects]] + digest = "1:e263726ba0d84e5ab1d9b96de99ab84249c83aea493c3dabfc652480189c8c7c" name = "github.com/fatih/camelcase" packages = ["."] + pruneopts = "UT" revision = "f6a740d52f961c60348ebb109adde9f4635d7540" [[projects]] + digest = "1:c45cef8e0074ea2f8176a051df38553ba997a3616f1ec2d35222b1cf9864881e" name = "github.com/ghodss/yaml" packages = ["."] + pruneopts = "UT" revision = "73d445a93680fa1a78ae23a5839bad48f32ba1ee" [[projects]] + digest = "1:172569c4bdc486213be0121e6039df4c272e9ff29397d9fd3716c31e4b37e15d" name = "github.com/go-openapi/jsonpointer" packages = ["."] + pruneopts = "UT" revision = "46af16f9f7b149af66e5d1bd010e3574dc06de98" [[projects]] + digest = "1:f30ccde775458301b306f4576e11de88d3ed0d91e68a5f3591c4ed8afbca76fa" name = "github.com/go-openapi/jsonreference" packages = ["."] + pruneopts = "UT" revision = "13c6e3589ad90f49bd3e3bbe2c2cb3d7a4142272" [[projects]] + digest = "1:ed3642d1497a543323f731039927aef565de45bafaffc97d138d7dc5bc14b5b5" name = "github.com/go-openapi/spec" packages = ["."] + pruneopts = "UT" revision = "1de3e0542de65ad8d75452a595886fdd0befb363" [[projects]] + digest = "1:3a42f9cbdeb4db3a14e0c3bb35852b7426b69f73386d52b606baf5d0fecfb4d7" name = "github.com/go-openapi/swag" packages = ["."] + pruneopts = "UT" revision = "f3f9494671f93fcff853e3c6e9e948b3eb71e590" [[projects]] + digest = "1:9ae31ce33b4bab257668963e844d98765b44160be4ee98cafc44637a213e530d" name = "github.com/gobwas/glob" packages = [ ".", @@ -197,66 +262,84 @@ "syntax/ast", "syntax/lexer", "util/runes", - "util/strings" + "util/strings", ] + pruneopts = "UT" revision = "5ccd90ef52e1e632236f7326478d4faa74f99438" version = "v0.2.3" [[projects]] + digest = "1:f83d740263b44fdeef3e1bce6147b5d7283fcad1a693d39639be33993ecf3db1" name = "github.com/gogo/protobuf" packages = [ "proto", - "sortkeys" + "sortkeys", ] + pruneopts = "UT" revision = "c0656edd0d9eab7c66d1eb0c568f9039345796f7" [[projects]] + digest = "1:2edd2416f89b4e841df0e4a78802ce14d2bc7ad79eba1a45986e39f0f8cb7d87" name = "github.com/golang/glog" packages = ["."] + pruneopts = "UT" revision = "44145f04b68cf362d9c4df2182967c2275eaefed" [[projects]] + digest = "1:7672c206322f45b33fac1ae2cb899263533ce0adcc6481d207725560208ec84e" name = "github.com/golang/groupcache" packages = ["lru"] + pruneopts = "UT" revision = "02826c3e79038b59d737d3b1c0a1d937f71a4433" [[projects]] + digest = "1:8f2df6167daef6f4d56d07f99bbcf4733117db0dedfd959995b9a679c52561f1" name = "github.com/golang/protobuf" packages = [ "proto", "ptypes", "ptypes/any", "ptypes/duration", - "ptypes/timestamp" + "ptypes/timestamp", ] + pruneopts = "UT" revision = "1643683e1b54a9e88ad26d98f81400c8c9d9f4f9" [[projects]] + digest = "1:62dfb39fe3bddeabb02cc001075ed9f951b044da2cd5b0f970ca798b1553bac3" name = "github.com/google/btree" packages = ["."] + pruneopts = "UT" revision = "7d79101e329e5a3adf994758c578dab82b90c017" [[projects]] + digest = "1:41bfd4219241b7f7d6e6fdb13fc712576f1337e68e6b895136283b76928fdd66" name = "github.com/google/gofuzz" packages = ["."] + pruneopts = "UT" revision = "44d81051d367757e1c7c6a5a86423ece9afcf63c" [[projects]] + digest = "1:8f8811f9be822914c3a25c6a071e93beb4c805d7b026cbf298bc577bc1cc945b" name = "github.com/google/uuid" packages = ["."] + pruneopts = "UT" revision = "064e2069ce9c359c118179501254f67d7d37ba24" version = "0.2" [[projects]] + digest = "1:75eb87381d25cc75212f52358df9c3a2719584eaa9685cd510ce28699122f39d" name = "github.com/googleapis/gnostic" packages = [ "OpenAPIv2", "compiler", - "extensions" + "extensions", ] + pruneopts = "UT" revision = "0c5108395e2debce0d731cf0287ddf7242066aba" [[projects]] + digest = "1:5c535c32658f994bae105a266379a40246a0aa8bfde5e78093a331f9a0b3cdb7" name = "github.com/gophercloud/gophercloud" packages = [ ".", @@ -265,205 +348,221 @@ "openstack/identity/v2/tokens", "openstack/identity/v3/tokens", "openstack/utils", - "pagination" + "pagination", ] + pruneopts = "UT" revision = "6da026c32e2d622cc242d32984259c77237aefe1" [[projects]] branch = "master" + digest = "1:4e08dc2383a46b3107f0b34ca338c4459e8fc8ee90e46a60e728aa8a2b21d558" name = "github.com/gosuri/uitable" packages = [ ".", "util/strutil", - "util/wordwrap" + "util/wordwrap", ] + pruneopts = "UT" revision = "36ee7e946282a3fb1cfecd476ddc9b35d8847e42" [[projects]] + digest = "1:878f0defa9b853f9acfaf4a162ba450a89d0050eff084f9fe7f5bd15948f172a" name = "github.com/gregjones/httpcache" packages = [ ".", - "diskcache" + "diskcache", ] + pruneopts = "UT" revision = "787624de3eb7bd915c329cba748687a3b22666a6" [[projects]] + digest = "1:3f90d23757c18b1e07bf11494dbe737ee2c44d881c0f41e681611abdadad62fa" name = "github.com/hashicorp/golang-lru" packages = [ ".", - "simplelru" + "simplelru", ] + pruneopts = "UT" revision = "a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4" [[projects]] - branch = "master" - name = "github.com/howeyc/gopass" - packages = ["."] - revision = "bf9dde6d0d2c004a008c27aaee91170c786f6db8" - -[[projects]] + digest = "1:80544abec6a93301c477926d6ed12dffce5029ddb34101435d88277f98006844" name = "github.com/huandu/xstrings" packages = ["."] + pruneopts = "UT" revision = "3959339b333561bf62a38b424fd41517c2c90f40" [[projects]] + digest = "1:8eb1de8112c9924d59bf1d3e5c26f5eaa2bfc2a5fcbb92dc1c2e4546d695f277" name = "github.com/imdario/mergo" packages = ["."] - revision = "6633656539c1639d9d78127b7d47c622b5d7b6dc" + pruneopts = "UT" + revision = "9f23e2d6bd2a77f959b2bf6acdbefd708a83a4a4" + version = "v0.3.6" [[projects]] + digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be" name = "github.com/inconshreveable/mousetrap" packages = ["."] + pruneopts = "UT" revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" version = "v1.0" [[projects]] + digest = "1:bb3cc4c1b21ea18cfa4e3e47440fc74d316ab25b0cf42927e8c1274917bd9891" name = "github.com/json-iterator/go" packages = ["."] - revision = "13f86432b882000a51c6e610c620974462691a97" + pruneopts = "UT" + revision = "f2b4162afba35581b6d4a50d3b8f34e33c144682" [[projects]] + digest = "1:a867990aee2ebc1ac86614ed702bf1e63061a79eac12d4326203cb9084b61839" name = "github.com/mailru/easyjson" packages = [ "buffer", "jlexer", - "jwriter" + "jwriter", ] + pruneopts = "UT" revision = "2f5df55504ebc322e4d52d34df6a1f5b503bf26d" [[projects]] + digest = "1:1583473db99b1057f15e6acf80fee5848c055aad49614f56862690aadcb87694" name = "github.com/mattn/go-runewidth" packages = ["."] + pruneopts = "UT" revision = "d6bea18f789704b5f83375793155289da36a3c7f" version = "v0.0.1" [[projects]] + digest = "1:7efe48dea4db6b35dcc15e15394b627247e5b3fb814242de986b746ba8e0abf0" name = "github.com/mattn/go-shellwords" packages = ["."] + pruneopts = "UT" revision = "02e3cf038dcea8290e44424da473dd12be796a8a" version = "v1.0.3" -[[projects]] - name = "github.com/matttproud/golang_protobuf_extensions" - packages = ["pbutil"] - revision = "fc2b8d3a73c4867e51861bbdd5ae3c1f0869dd6a" - [[projects]] branch = "master" + digest = "1:e68cd472b96cdf7c9f6971ac41bcc1d4d3b23d67c2a31d2399446e295bc88ae9" name = "github.com/mitchellh/go-wordwrap" packages = ["."] + pruneopts = "UT" revision = "ad45545899c7b13c020ea92b2072220eefad42b8" [[projects]] + digest = "1:33422d238f147d247752996a26574ac48dcf472976eda7f5134015f06bf16563" + name = "github.com/modern-go/concurrent" + packages = ["."] + pruneopts = "UT" + revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94" + version = "1.0.3" + +[[projects]] + digest = "1:e32bdbdb7c377a07a9a46378290059822efdce5c8d96fe71940d87cb4f918855" + name = "github.com/modern-go/reflect2" + packages = ["."] + pruneopts = "UT" + revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd" + version = "1.0.1" + +[[projects]] + digest = "1:37423212694a4316f48e0bbac8e4f1fd366a384a286fbaa7d80baf99d86f0416" name = "github.com/opencontainers/go-digest" packages = ["."] + pruneopts = "UT" revision = "a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb" [[projects]] + digest = "1:8e1d3df780654a0c2227b1a4d6f11bfb46d386237f31cc8b5ae8dfa13b55b4ee" name = "github.com/opencontainers/image-spec" packages = [ "specs-go", - "specs-go/v1" + "specs-go/v1", ] + pruneopts = "UT" revision = "372ad780f63454fbbbbcc7cf80e5b90245c13e13" -[[projects]] - name = "github.com/pborman/uuid" - packages = ["."] - revision = "ca53cad383cad2479bbba7f7a1a05797ec1386e4" - [[projects]] branch = "master" + digest = "1:3bf17a6e6eaa6ad24152148a631d18662f7212e21637c2699bff3369b7f00fa2" name = "github.com/petar/GoLLRB" packages = ["llrb"] + pruneopts = "UT" revision = "53be0d36a84c2a886ca057d34b6aa4468df9ccb4" [[projects]] + digest = "1:0e7775ebbcf00d8dd28ac663614af924411c868dca3d5aa762af0fae3808d852" name = "github.com/peterbourgon/diskv" packages = ["."] + pruneopts = "UT" revision = "5f041e8faa004a95c88a202771f4cc3e991971e6" version = "v2.0.1" [[projects]] + digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747" name = "github.com/pkg/errors" packages = ["."] + pruneopts = "UT" revision = "645ef00459ed84a119197bfb8d8205042c6df63d" version = "v0.8.0" [[projects]] + digest = "1:08413c4235cad94a96c39e1e2f697789733c4a87d1fdf06b412d2cf2ba49826a" name = "github.com/pmezard/go-difflib" packages = ["difflib"] + pruneopts = "UT" revision = "d8ed2627bdf02c080bf22230dbb337003b7aba2d" [[projects]] - name = "github.com/prometheus/client_golang" - packages = ["prometheus"] - revision = "e7e903064f5e9eb5da98208bae10b475d4db0f8c" - -[[projects]] - name = "github.com/prometheus/client_model" - packages = ["go"] - revision = "fa8ad6fec33561be4280a8f0514318c79d7f6cb6" - -[[projects]] - name = "github.com/prometheus/common" - packages = [ - "expfmt", - "internal/bitbucket.org/ww/goautoneg", - "model" - ] - revision = "13ba4ddd0caa9c28ca7b7bffe1dfa9ed8d5ef207" - -[[projects]] - name = "github.com/prometheus/procfs" - packages = [ - ".", - "xfs" - ] - revision = "65c1f6f8f0fc1e2185eb9863a3bc751496404259" - -[[projects]] + digest = "1:f78dee1142c1e43c9288534cadfa82f21dfd9a1163b06fa0fdf872f8020f2a53" name = "github.com/russross/blackfriday" packages = ["."] + pruneopts = "UT" revision = "300106c228d52c8941d4b3de6054a6062a86dda3" [[projects]] + digest = "1:166006f557f8035424fad136d1806d5c73229e82c670500dcbfba1a1160f5ddb" name = "github.com/shurcooL/sanitized_anchor_name" packages = ["."] + pruneopts = "UT" revision = "10ef21a441db47d8b13ebcc5fd2310f636973c77" [[projects]] + digest = "1:fb011abd58a582cf867409273f372fc6437eda670ff02055c47e6203e90466d7" name = "github.com/sirupsen/logrus" packages = ["."] + pruneopts = "UT" revision = "89742aefa4b206dcf400792f3bd35b542998eb3b" [[projects]] + digest = "1:f56a38901e3d06fb5c71219d4e5b48d546d845f776d6219097733ec27011dc60" name = "github.com/spf13/cobra" packages = [ ".", - "doc" + "doc", ] + pruneopts = "UT" revision = "a1f051bc3eba734da4772d60e2d677f47cf93ef4" version = "v0.0.2" [[projects]] + digest = "1:9424f440bba8f7508b69414634aef3b2b3a877e522d8a4624692412805407bb7" name = "github.com/spf13/pflag" packages = ["."] + pruneopts = "UT" revision = "583c0c0531f06d5278b7d917446061adc344b5cd" version = "v1.0.1" [[projects]] + digest = "1:ab5a3e72b1d94f9f7baa42963939a21ab5ab8e26976cd83ecf7da1a9cbbc7096" name = "github.com/stretchr/testify" packages = ["assert"] + pruneopts = "UT" revision = "e3a8ff8ce36581f87a15341206f205b1da467059" [[projects]] - branch = "master" - name = "github.com/technosophos/moniker" - packages = ["."] - revision = "ab470f5e105a44d0c87ea21bacd6a335c4816d83" - -[[projects]] + digest = "1:9601e4354239b69f62c86d24c74a19d7c7e3c7f7d2d9f01d42e5830b4673e121" name = "golang.org/x/crypto" packages = [ "cast5", @@ -478,11 +577,13 @@ "openpgp/s2k", "pbkdf2", "scrypt", - "ssh/terminal" + "ssh/terminal", ] + pruneopts = "UT" revision = "81e90905daefcd6fd217b62423c0908922eadb30" [[projects]] + digest = "1:1e853578c8a3c5d54c1b54a4821075393b032110170107295f75442f8b41720c" name = "golang.org/x/net" packages = [ "context", @@ -490,30 +591,36 @@ "http2", "http2/hpack", "idna", - "lex/httplex" + "lex/httplex", ] + pruneopts = "UT" revision = "1c05540f6879653db88113bc4a2b70aec4bd491f" [[projects]] + digest = "1:ad764db92ed977f803ff0f59a7a957bf65cc4e8ae9dfd08228e1f54ea40392e0" name = "golang.org/x/oauth2" packages = [ ".", "google", "internal", "jws", - "jwt" + "jwt", ] + pruneopts = "UT" revision = "a6bd8cefa1811bd24b86f8902872e4e8225f74c4" [[projects]] + digest = "1:4ee37ffda2d3e007c5215ad02b56b845f20ea479ee69faa4120e8a767efcc757" name = "golang.org/x/sys" packages = [ "unix", - "windows" + "windows", ] + pruneopts = "UT" revision = "43eea11bc92608addb41b8a406b0407495c106f6" [[projects]] + digest = "1:16cd7c873369dc2c42155cad1bc9ea83409e52e3b68f185a3084fb6b84007465" name = "golang.org/x/text" packages = [ "cases", @@ -536,16 +643,20 @@ "unicode/cldr", "unicode/norm", "unicode/rangetable", - "width" + "width", ] + pruneopts = "UT" revision = "b19bf474d317b857955b12035d2c5acb57ce8b01" [[projects]] + digest = "1:d37b0ef2944431fe9e8ef35c6fffc8990d9e2ca300588df94a6890f3649ae365" name = "golang.org/x/time" packages = ["rate"] + pruneopts = "UT" revision = "f51c12702a4d776e4c1fa9b0fabab841babae631" [[projects]] + digest = "1:da33412a421eff87565c54a3223d9aeaf87ec3fc7344fc291cadd9c74113de46" name = "google.golang.org/appengine" packages = [ ".", @@ -557,34 +668,41 @@ "internal/modules", "internal/remote_api", "internal/urlfetch", - "urlfetch" + "urlfetch", ] + pruneopts = "UT" revision = "12d5545dc1cfa6047a286d5e853841b6471f4c19" [[projects]] + digest = "1:ef72505cf098abdd34efeea032103377bec06abb61d8a06f002d5d296a4b1185" name = "gopkg.in/inf.v0" packages = ["."] + pruneopts = "UT" revision = "3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4" version = "v0.9.0" [[projects]] + digest = "1:517fc596d15da8456fefaa85bcff18dd2068ade58f1e108997c2456ff0e83d3d" name = "gopkg.in/square/go-jose.v2" packages = [ ".", "cipher", "json", - "jwt" + "jwt", ] - revision = "f8f38de21b4dcd69d0413faf231983f5fd6634b1" - version = "v2.1.3" + pruneopts = "UT" + revision = "89060dee6a84df9a4dae49f676f0c755037834f1" [[projects]] + digest = "1:c27797c5f42d349e2a604510822df7d037415aae58bf1e6fd35624eda757c0aa" name = "gopkg.in/yaml.v2" packages = ["."] + pruneopts = "UT" revision = "53feefa2559fb8dfa8d81baad31be332c97d6c77" [[projects]] - branch = "release-1.10" + branch = "release-1.12" + digest = "1:16e3493f1ebd6e2c9bf2f05a2c0f0f23bd8bd346dfa27bfc5ccd29d2b77f900c" name = "k8s.io/api" packages = [ "admission/v1beta1", @@ -599,10 +717,12 @@ "authorization/v1beta1", "autoscaling/v1", "autoscaling/v2beta1", + "autoscaling/v2beta2", "batch/v1", "batch/v1beta1", "batch/v2alpha1", "certificates/v1beta1", + "coordination/v1beta1", "core/v1", "events/v1beta1", "extensions/v1beta1", @@ -613,33 +733,38 @@ "rbac/v1alpha1", "rbac/v1beta1", "scheduling/v1alpha1", + "scheduling/v1beta1", "settings/v1alpha1", "storage/v1", "storage/v1alpha1", - "storage/v1beta1" + "storage/v1beta1", ] - revision = "c699ec51538f0cfd4afa8bfcfe1e0779cafbe666" + pruneopts = "UT" + revision = "475331a8afff5587f47d0470a93f79c60c573c03" [[projects]] + digest = "1:b46a162d7c7e9117ae2dd9a73ee4dc2181ad9ea9d505fd7c5eb63c96211dc9dd" name = "k8s.io/apiextensions-apiserver" packages = ["pkg/features"] + pruneopts = "UT" revision = "898b0eda132e1aeac43a459785144ee4bf9b0a2e" [[projects]] - branch = "release-1.10" + branch = "release-1.12" + digest = "1:51d5c9bf991053e2c265cf2203c843dbddfa78fed4d8b22b9072b50561accd2b" name = "k8s.io/apimachinery" packages = [ "pkg/api/equality", "pkg/api/errors", "pkg/api/meta", + "pkg/api/meta/testrestmapper", "pkg/api/resource", "pkg/api/validation", - "pkg/apimachinery", - "pkg/apimachinery/announced", - "pkg/apimachinery/registered", + "pkg/api/validation/path", "pkg/apis/meta/internalversion", "pkg/apis/meta/v1", "pkg/apis/meta/v1/unstructured", + "pkg/apis/meta/v1/unstructured/unstructuredscheme", "pkg/apis/meta/v1/validation", "pkg/apis/meta/v1beta1", "pkg/conversion", @@ -667,13 +792,13 @@ "pkg/util/intstr", "pkg/util/json", "pkg/util/mergepatch", + "pkg/util/naming", "pkg/util/net", "pkg/util/rand", "pkg/util/remotecommand", "pkg/util/runtime", "pkg/util/sets", "pkg/util/strategicpatch", - "pkg/util/uuid", "pkg/util/validation", "pkg/util/validation/field", "pkg/util/wait", @@ -682,12 +807,14 @@ "pkg/watch", "third_party/forked/golang/json", "third_party/forked/golang/netutil", - "third_party/forked/golang/reflect" + "third_party/forked/golang/reflect", ] - revision = "54101a56dda9a0962bc48751c058eb4c546dcbb9" + pruneopts = "UT" + revision = "6dd46049f39503a1fc8d65de4bd566829e95faff" [[projects]] - branch = "release-1.10" + branch = "release-1.12" + digest = "1:e38fd46b96594c848ae465219e948e3ca1d3b595fc136d63b5022e625d8436bb" name = "k8s.io/apiserver" packages = [ "pkg/apis/audit", @@ -697,56 +824,30 @@ "pkg/endpoints/request", "pkg/features", "pkg/util/feature", - "pkg/util/flag" ] - revision = "ea53f8588c655568158b4ff53f5ec6fa4ebfc332" + pruneopts = "UT" + revision = "a4ed22a224c0a5f970851abac59d313a6ac803c5" [[projects]] + branch = "master" + digest = "1:9fefce8370f58ca7701e82309446bd72ca765a1c5da83207857cebbc1bd9aeb2" + name = "k8s.io/cli-runtime" + packages = [ + "pkg/genericclioptions", + "pkg/genericclioptions/printers", + "pkg/genericclioptions/resource", + ] + pruneopts = "UT" + revision = "7915b4409361b23932e7af013a2ec31d5753e001" + +[[projects]] + digest = "1:fb69e389e81d571d59b7a22ac06354e4cfec2a1d693362117b330977cad8d69a" name = "k8s.io/client-go" packages = [ "discovery", "discovery/fake", "dynamic", - "informers", - "informers/admissionregistration", - "informers/admissionregistration/v1alpha1", - "informers/admissionregistration/v1beta1", - "informers/apps", - "informers/apps/v1", - "informers/apps/v1beta1", - "informers/apps/v1beta2", - "informers/autoscaling", - "informers/autoscaling/v1", - "informers/autoscaling/v2beta1", - "informers/batch", - "informers/batch/v1", - "informers/batch/v1beta1", - "informers/batch/v2alpha1", - "informers/certificates", - "informers/certificates/v1beta1", - "informers/core", - "informers/core/v1", - "informers/events", - "informers/events/v1beta1", - "informers/extensions", - "informers/extensions/v1beta1", - "informers/internalinterfaces", - "informers/networking", - "informers/networking/v1", - "informers/policy", - "informers/policy/v1beta1", - "informers/rbac", - "informers/rbac/v1", - "informers/rbac/v1alpha1", - "informers/rbac/v1beta1", - "informers/scheduling", - "informers/scheduling/v1alpha1", - "informers/settings", - "informers/settings/v1alpha1", - "informers/storage", - "informers/storage/v1", - "informers/storage/v1alpha1", - "informers/storage/v1beta1", + "dynamic/fake", "kubernetes", "kubernetes/fake", "kubernetes/scheme", @@ -772,6 +873,8 @@ "kubernetes/typed/autoscaling/v1/fake", "kubernetes/typed/autoscaling/v2beta1", "kubernetes/typed/autoscaling/v2beta1/fake", + "kubernetes/typed/autoscaling/v2beta2", + "kubernetes/typed/autoscaling/v2beta2/fake", "kubernetes/typed/batch/v1", "kubernetes/typed/batch/v1/fake", "kubernetes/typed/batch/v1beta1", @@ -780,6 +883,8 @@ "kubernetes/typed/batch/v2alpha1/fake", "kubernetes/typed/certificates/v1beta1", "kubernetes/typed/certificates/v1beta1/fake", + "kubernetes/typed/coordination/v1beta1", + "kubernetes/typed/coordination/v1beta1/fake", "kubernetes/typed/core/v1", "kubernetes/typed/core/v1/fake", "kubernetes/typed/events/v1beta1", @@ -798,6 +903,8 @@ "kubernetes/typed/rbac/v1beta1/fake", "kubernetes/typed/scheduling/v1alpha1", "kubernetes/typed/scheduling/v1alpha1/fake", + "kubernetes/typed/scheduling/v1beta1", + "kubernetes/typed/scheduling/v1beta1/fake", "kubernetes/typed/settings/v1alpha1", "kubernetes/typed/settings/v1alpha1/fake", "kubernetes/typed/storage/v1", @@ -806,32 +913,9 @@ "kubernetes/typed/storage/v1alpha1/fake", "kubernetes/typed/storage/v1beta1", "kubernetes/typed/storage/v1beta1/fake", - "listers/admissionregistration/v1alpha1", - "listers/admissionregistration/v1beta1", - "listers/apps/v1", - "listers/apps/v1beta1", - "listers/apps/v1beta2", - "listers/autoscaling/v1", - "listers/autoscaling/v2beta1", - "listers/batch/v1", - "listers/batch/v1beta1", - "listers/batch/v2alpha1", - "listers/certificates/v1beta1", - "listers/core/v1", - "listers/events/v1beta1", - "listers/extensions/v1beta1", - "listers/networking/v1", - "listers/policy/v1beta1", - "listers/rbac/v1", - "listers/rbac/v1alpha1", - "listers/rbac/v1beta1", - "listers/scheduling/v1alpha1", - "listers/settings/v1alpha1", - "listers/storage/v1", - "listers/storage/v1alpha1", - "listers/storage/v1beta1", "pkg/apis/clientauthentication", "pkg/apis/clientauthentication/v1alpha1", + "pkg/apis/clientauthentication/v1beta1", "pkg/version", "plugin/pkg/client/auth", "plugin/pkg/client/auth/azure", @@ -842,6 +926,7 @@ "rest", "rest/fake", "rest/watch", + "restmapper", "scale", "scale/scheme", "scale/scheme/appsint", @@ -863,31 +948,37 @@ "tools/record", "tools/reference", "tools/remotecommand", + "tools/watch", "transport", "transport/spdy", "util/buffer", "util/cert", + "util/connrotation", "util/exec", "util/flowcontrol", "util/homedir", "util/integer", "util/jsonpath", "util/retry", - "util/workqueue" ] - revision = "23781f4d6632d88e869066eaebb743857aa1ef9b" - version = "kubernetes-1.10.0" + pruneopts = "UT" + revision = "cb4883f3dea0a8d72fc4af710798a980992a773d" + version = "kubernetes-1.12.1" [[projects]] + digest = "1:8a5fb6a585e27c0339096c0db745795940a7e72a7925e7c4cf40b76bd113d382" name = "k8s.io/kube-openapi" packages = [ "pkg/util/proto", - "pkg/util/proto/validation" + "pkg/util/proto/testing", + "pkg/util/proto/validation", ] + pruneopts = "UT" revision = "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" [[projects]] - branch = "release-1.10" + branch = "release-1.12" + digest = "1:fa66cfb06184d37bcf1fa085a91ba8f5c9d3a0c440f72c7bf898c3162a848617" name = "k8s.io/kubernetes" packages = [ "pkg/api/events", @@ -896,11 +987,7 @@ "pkg/api/ref", "pkg/api/resource", "pkg/api/service", - "pkg/api/testapi", "pkg/api/v1/pod", - "pkg/apis/admission", - "pkg/apis/admission/install", - "pkg/apis/admission/v1beta1", "pkg/apis/admissionregistration", "pkg/apis/admissionregistration/install", "pkg/apis/admissionregistration/v1alpha1", @@ -922,6 +1009,7 @@ "pkg/apis/autoscaling/install", "pkg/apis/autoscaling/v1", "pkg/apis/autoscaling/v2beta1", + "pkg/apis/autoscaling/v2beta2", "pkg/apis/batch", "pkg/apis/batch/install", "pkg/apis/batch/v1", @@ -930,9 +1018,9 @@ "pkg/apis/certificates", "pkg/apis/certificates/install", "pkg/apis/certificates/v1beta1", - "pkg/apis/componentconfig", - "pkg/apis/componentconfig/install", - "pkg/apis/componentconfig/v1alpha1", + "pkg/apis/coordination", + "pkg/apis/coordination/install", + "pkg/apis/coordination/v1beta1", "pkg/apis/core", "pkg/apis/core/helper", "pkg/apis/core/helper/qos", @@ -940,7 +1028,6 @@ "pkg/apis/core/pods", "pkg/apis/core/v1", "pkg/apis/core/v1/helper", - "pkg/apis/core/v1/helper/qos", "pkg/apis/core/validation", "pkg/apis/events", "pkg/apis/events/install", @@ -948,9 +1035,6 @@ "pkg/apis/extensions", "pkg/apis/extensions/install", "pkg/apis/extensions/v1beta1", - "pkg/apis/imagepolicy", - "pkg/apis/imagepolicy/install", - "pkg/apis/imagepolicy/v1alpha1", "pkg/apis/networking", "pkg/apis/networking/install", "pkg/apis/networking/v1", @@ -965,6 +1049,7 @@ "pkg/apis/scheduling", "pkg/apis/scheduling/install", "pkg/apis/scheduling/v1alpha1", + "pkg/apis/scheduling/v1beta1", "pkg/apis/settings", "pkg/apis/settings/install", "pkg/apis/settings/v1alpha1", @@ -984,6 +1069,7 @@ "pkg/client/clientset_generated/internalclientset/typed/autoscaling/internalversion", "pkg/client/clientset_generated/internalclientset/typed/batch/internalversion", "pkg/client/clientset_generated/internalclientset/typed/certificates/internalversion", + "pkg/client/clientset_generated/internalclientset/typed/coordination/internalversion", "pkg/client/clientset_generated/internalclientset/typed/core/internalversion", "pkg/client/clientset_generated/internalclientset/typed/events/internalversion", "pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion", @@ -993,36 +1079,27 @@ "pkg/client/clientset_generated/internalclientset/typed/scheduling/internalversion", "pkg/client/clientset_generated/internalclientset/typed/settings/internalversion", "pkg/client/clientset_generated/internalclientset/typed/storage/internalversion", - "pkg/cloudprovider", "pkg/controller", - "pkg/controller/daemon", - "pkg/controller/daemon/util", "pkg/controller/deployment/util", - "pkg/controller/history", - "pkg/controller/statefulset", - "pkg/controller/volume/events", - "pkg/controller/volume/persistentvolume", - "pkg/controller/volume/persistentvolume/metrics", "pkg/credentialprovider", "pkg/features", "pkg/fieldpath", + "pkg/generated", "pkg/kubectl", "pkg/kubectl/apps", - "pkg/kubectl/categories", + "pkg/kubectl/cmd/get", "pkg/kubectl/cmd/templates", "pkg/kubectl/cmd/testing", "pkg/kubectl/cmd/util", "pkg/kubectl/cmd/util/openapi", "pkg/kubectl/cmd/util/openapi/testing", "pkg/kubectl/cmd/util/openapi/validation", - "pkg/kubectl/plugins", - "pkg/kubectl/resource", "pkg/kubectl/scheme", "pkg/kubectl/util", "pkg/kubectl/util/hash", + "pkg/kubectl/util/i18n", "pkg/kubectl/util/slice", "pkg/kubectl/util/term", - "pkg/kubectl/util/transport", "pkg/kubectl/validation", "pkg/kubelet/apis", "pkg/kubelet/types", @@ -1031,52 +1108,110 @@ "pkg/printers/internalversion", "pkg/registry/rbac/validation", "pkg/scheduler/algorithm", - "pkg/scheduler/algorithm/predicates", "pkg/scheduler/algorithm/priorities/util", "pkg/scheduler/api", - "pkg/scheduler/schedulercache", + "pkg/scheduler/cache", "pkg/scheduler/util", - "pkg/scheduler/volumebinder", "pkg/security/apparmor", "pkg/serviceaccount", "pkg/util/file", - "pkg/util/goroutinemap", - "pkg/util/goroutinemap/exponentialbackoff", "pkg/util/hash", "pkg/util/interrupt", - "pkg/util/io", "pkg/util/labels", - "pkg/util/metrics", - "pkg/util/mount", "pkg/util/net/sets", "pkg/util/node", - "pkg/util/nsenter", "pkg/util/parsers", - "pkg/util/pointer", "pkg/util/slice", "pkg/util/taints", "pkg/version", - "pkg/volume", - "pkg/volume/util", - "pkg/volume/util/fs", - "pkg/volume/util/recyclerclient", - "pkg/volume/util/types" ] - revision = "a7685bbc127ba77463c89e363c5cec0d94a5f485" + pruneopts = "UT" + revision = "8ea5d6e20648a2bdf056105a75e7307c9e15c3f2" [[projects]] + branch = "master" + digest = "1:b587a79602928eab5971377f9fddc392cd047202ac4d6aae8845e10bd8634d78" name = "k8s.io/utils" - packages = ["exec"] - revision = "aedf551cdb8b0119df3a19c65fde413a13b34997" + packages = [ + "exec", + "pointer", + ] + pruneopts = "UT" + revision = "f024bbd3a09501e18d1973b22be7188c5c005014" [[projects]] + digest = "1:96f9b7c99c55e6063371088376d57d398f42888dedd08ab5d35065aba11e3965" name = "vbom.ml/util" packages = ["sortorder"] + pruneopts = "UT" revision = "db5cfe13f5cc80a4990d98e2e1b0707a4d1a5394" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "4a0464d8de132c8a733f50549ad69e663b992e12537b58626302dc5dd14cd3f0" + input-imports = [ + "github.com/BurntSushi/toml", + "github.com/Masterminds/semver", + "github.com/Masterminds/sprig", + "github.com/Masterminds/vcs", + "github.com/asaskevich/govalidator", + "github.com/evanphx/json-patch", + "github.com/ghodss/yaml", + "github.com/gobwas/glob", + "github.com/gosuri/uitable", + "github.com/gosuri/uitable/util/strutil", + "github.com/mattn/go-shellwords", + "github.com/pkg/errors", + "github.com/spf13/cobra", + "github.com/spf13/cobra/doc", + "github.com/spf13/pflag", + "github.com/stretchr/testify/assert", + "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/equality", + "k8s.io/apimachinery/pkg/api/errors", + "k8s.io/apimachinery/pkg/api/meta", + "k8s.io/apimachinery/pkg/apis/meta/v1", + "k8s.io/apimachinery/pkg/fields", + "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/strategicpatch", + "k8s.io/apimachinery/pkg/util/validation", + "k8s.io/apimachinery/pkg/util/wait", + "k8s.io/apimachinery/pkg/version", + "k8s.io/apimachinery/pkg/watch", + "k8s.io/cli-runtime/pkg/genericclioptions", + "k8s.io/cli-runtime/pkg/genericclioptions/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/fake", + "k8s.io/client-go/tools/clientcmd", + "k8s.io/client-go/tools/watch", + "k8s.io/client-go/util/homedir", + "k8s.io/kubernetes/pkg/api/legacyscheme", + "k8s.io/kubernetes/pkg/apis/batch", + "k8s.io/kubernetes/pkg/controller/deployment/util", + "k8s.io/kubernetes/pkg/kubectl/cmd/get", + "k8s.io/kubernetes/pkg/kubectl/cmd/testing", + "k8s.io/kubernetes/pkg/kubectl/cmd/util", + "k8s.io/kubernetes/pkg/kubectl/scheme", + "k8s.io/kubernetes/pkg/kubectl/validation", + ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 83096a814..e1f48dc40 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -27,25 +27,41 @@ name = "github.com/gosuri/uitable" branch = "master" -[[constraint]] - name = "github.com/technosophos/moniker" - branch = "master" - [[constraint]] name = "k8s.io/api" - branch = "release-1.10" + branch = "release-1.12" [[constraint]] name = "k8s.io/apimachinery" - branch = "release-1.10" + branch = "release-1.12" [[constraint]] - version = "kubernetes-1.10.0" + version = "kubernetes-1.12.1" name = "k8s.io/client-go" [[constraint]] name = "k8s.io/kubernetes" - branch = "release-1.10" + branch = "release-1.12" + +[[override]] + name = "k8s.io/apiserver" + branch = "release-1.12" + +[[override]] + name = "github.com/json-iterator/go" + revision = "f2b4162afba35581b6d4a50d3b8f34e33c144682" + +[[override]] + name = "github.com/Azure/go-autorest" + revision = "1ff28809256a84bb6966640ff3d0371af82ccba4" + +[[override]] + name = "github.com/imdario/mergo" + version = "v0.3.5" + +[[override]] + name = "gopkg.in/square/go-jose.v2" + revision = "89060dee6a84df9a4dae49f676f0c755037834f1" [prune] go-tests = true diff --git a/Makefile b/Makefile index 394a4a2e0..1fbc5ecf4 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ BINDIR := $(CURDIR)/bin DIST_DIRS := find * -type d -exec TARGETS := darwin/amd64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le windows/amd64 +BINNAME ?= helm # go option GO ?= go @@ -41,12 +42,12 @@ all: build .PHONY: build build: - $(GO) build $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o $(BINDIR)/helm k8s.io/helm/cmd/helm + $(GO) build $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o $(BINDIR)/$(BINNAME) k8s.io/helm/cmd/helm .PHONY: build-cross build-cross: LDFLAGS += -extldflags "-static" build-cross: - CGO_ENABLED=0 gox -parallel=3 -output="_dist/{{.OS}}-{{.Arch}}/{{.Dir}}" -osarch='$(TARGETS)' $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' k8s.io/helm/cmd/helm + CGO_ENABLED=0 gox -parallel=3 -output="_dist/{{.OS}}-{{.Arch}}/$(BINNAME)" -osarch='$(TARGETS)' $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' k8s.io/helm/cmd/helm .PHONY: dist dist: diff --git a/README.md b/README.md index fb2e16bce..bbf6f2d05 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,8 @@ Use Helm to: Helm is a tool that streamlines installing and managing Kubernetes applications. Think of it like apt/yum/homebrew for Kubernetes. -- Helm has two parts: a client (`helm`) and a server (`tiller`) -- Tiller runs inside of your Kubernetes cluster, and manages releases (installations) - of your charts. +- Helm has two parts: a client (`helm`) and a library +- The library renders your templates and communicates with the Kubernetes API - Helm runs on your laptop, CI/CD, or wherever you want it to run. - Charts are Helm packages that contain at least two things: - A description of the package (`Chart.yaml`) diff --git a/cmd/helm/completion.go b/cmd/helm/completion.go index 3685c42cc..03ab63017 100644 --- a/cmd/helm/completion.go +++ b/cmd/helm/completion.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/cmd/helm/create.go b/cmd/helm/create.go index 6e63fd305..e6a91b121 100644 --- a/cmd/helm/create.go +++ b/cmd/helm/create.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -24,8 +24,8 @@ import ( "github.com/spf13/cobra" "k8s.io/helm/cmd/helm/require" + "k8s.io/helm/pkg/chart" "k8s.io/helm/pkg/chartutil" - "k8s.io/helm/pkg/hapi/chart" ) const createDesc = ` @@ -80,7 +80,7 @@ func (o *createOptions) run(out io.Writer) error { Description: "A Helm chart for Kubernetes", Version: "0.1.0", AppVersion: "1.0", - APIVersion: chartutil.APIVersionv1, + APIVersion: chart.APIVersionv1, } if o.starter != "" { diff --git a/cmd/helm/create_test.go b/cmd/helm/create_test.go index 5ec69f678..7f23c1f6e 100644 --- a/cmd/helm/create_test.go +++ b/cmd/helm/create_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -23,8 +23,9 @@ import ( "path/filepath" "testing" + "k8s.io/helm/pkg/chart" + "k8s.io/helm/pkg/chart/loader" "k8s.io/helm/pkg/chartutil" - "k8s.io/helm/pkg/hapi/chart" ) func TestCreateCmd(t *testing.T) { @@ -46,15 +47,15 @@ func TestCreateCmd(t *testing.T) { t.Fatalf("chart is not directory") } - c, err := chartutil.LoadDir(cname) + c, err := loader.LoadDir(cname) if err != nil { t.Fatal(err) } - if c.Metadata.Name != cname { - t.Errorf("Expected %q name, got %q", cname, c.Metadata.Name) + if c.Name() != cname { + t.Errorf("Expected %q name, got %q", cname, c.Name()) } - if c.Metadata.APIVersion != chartutil.APIVersionv1 { + if c.Metadata.APIVersion != chart.APIVersionv1 { t.Errorf("Wrong API version: %q", c.Metadata.APIVersion) } } @@ -97,15 +98,15 @@ func TestCreateStarterCmd(t *testing.T) { t.Fatalf("chart is not directory") } - c, err := chartutil.LoadDir(cname) + c, err := loader.LoadDir(cname) if err != nil { t.Fatal(err) } - if c.Metadata.Name != cname { - t.Errorf("Expected %q name, got %q", cname, c.Metadata.Name) + if c.Name() != cname { + t.Errorf("Expected %q name, got %q", cname, c.Name()) } - if c.Metadata.APIVersion != chartutil.APIVersionv1 { + if c.Metadata.APIVersion != chart.APIVersionv1 { t.Errorf("Wrong API version: %q", c.Metadata.APIVersion) } diff --git a/cmd/helm/dependency.go b/cmd/helm/dependency.go index e31c26000..ff88150cd 100644 --- a/cmd/helm/dependency.go +++ b/cmd/helm/dependency.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 @@ -26,7 +26,8 @@ import ( "github.com/spf13/cobra" "k8s.io/helm/cmd/helm/require" - "k8s.io/helm/pkg/chartutil" + "k8s.io/helm/pkg/chart" + "k8s.io/helm/pkg/chart/loader" ) const dependencyDesc = ` @@ -130,27 +131,23 @@ func newDependencyListCmd(out io.Writer) *cobra.Command { } func (o *dependencyLisOptions) run(out io.Writer) error { - c, err := chartutil.Load(o.chartpath) + c, err := loader.Load(o.chartpath) if err != nil { return err } - r, err := chartutil.LoadRequirements(c) - if err != nil { - if err == chartutil.ErrRequirementsNotFound { - fmt.Fprintf(out, "WARNING: no requirements at %s/charts\n", o.chartpath) - return nil - } - return err + if c.Metadata.Requirements == nil { + fmt.Fprintf(out, "WARNING: no requirements at %s/charts\n", o.chartpath) + return nil } - o.printRequirements(out, r) + o.printRequirements(out, c.Metadata.Requirements) fmt.Fprintln(out) - o.printMissing(out, r) + o.printMissing(out, c.Metadata.Requirements) return nil } -func (o *dependencyLisOptions) dependencyStatus(dep *chartutil.Dependency) string { +func (o *dependencyLisOptions) dependencyStatus(dep *chart.Dependency) string { filename := fmt.Sprintf("%s-%s.tgz", dep.Name, "*") archives, err := filepath.Glob(filepath.Join(o.chartpath, "charts", filename)) if err != nil { @@ -160,11 +157,11 @@ func (o *dependencyLisOptions) dependencyStatus(dep *chartutil.Dependency) strin } else if len(archives) == 1 { archive := archives[0] if _, err := os.Stat(archive); err == nil { - c, err := chartutil.Load(archive) + c, err := loader.Load(archive) if err != nil { return "corrupt" } - if c.Metadata.Name != dep.Name { + if c.Name() != dep.Name { return "misnamed" } @@ -195,12 +192,12 @@ func (o *dependencyLisOptions) dependencyStatus(dep *chartutil.Dependency) strin return "mispackaged" } - c, err := chartutil.Load(folder) + c, err := loader.Load(folder) if err != nil { return "corrupt" } - if c.Metadata.Name != dep.Name { + if c.Name() != dep.Name { return "misnamed" } @@ -225,18 +222,18 @@ func (o *dependencyLisOptions) dependencyStatus(dep *chartutil.Dependency) strin } // printRequirements prints all of the requirements in the yaml file. -func (o *dependencyLisOptions) printRequirements(out io.Writer, reqs *chartutil.Requirements) { +func (o *dependencyLisOptions) printRequirements(out io.Writer, reqs []*chart.Dependency) { table := uitable.New() table.MaxColWidth = 80 table.AddRow("NAME", "VERSION", "REPOSITORY", "STATUS") - for _, row := range reqs.Dependencies { + for _, row := range reqs { table.AddRow(row.Name, row.Version, row.Repository, o.dependencyStatus(row)) } fmt.Fprintln(out, table) } // printMissing prints warnings about charts that are present on disk, but are not in the requirements. -func (o *dependencyLisOptions) printMissing(out io.Writer, reqs *chartutil.Requirements) { +func (o *dependencyLisOptions) printMissing(out io.Writer, reqs []*chart.Dependency) { folder := filepath.Join(o.chartpath, "charts/*") files, err := filepath.Glob(folder) if err != nil { @@ -253,20 +250,20 @@ func (o *dependencyLisOptions) printMissing(out io.Writer, reqs *chartutil.Requi if !fi.IsDir() && filepath.Ext(f) != ".tgz" { continue } - c, err := chartutil.Load(f) + c, err := loader.Load(f) if err != nil { fmt.Fprintf(out, "WARNING: %q is not a chart.\n", f) continue } found := false - for _, d := range reqs.Dependencies { - if d.Name == c.Metadata.Name { + for _, d := range reqs { + if d.Name == c.Name() { found = true break } } if !found { - fmt.Fprintf(out, "WARNING: %q is not in requirements.yaml.\n", f) + fmt.Fprintf(out, "WARNING: %q is not in Chart.yaml.\n", f) } } diff --git a/cmd/helm/dependency_build.go b/cmd/helm/dependency_build.go index 06b534ab1..67fe7cf26 100644 --- a/cmd/helm/dependency_build.go +++ b/cmd/helm/dependency_build.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 @@ -26,7 +26,7 @@ import ( ) const dependencyBuildDesc = ` -Build out the charts/ directory from the requirements.lock file. +Build out the charts/ directory from the Chart.lock file. Build is used to reconstruct a chart's dependencies to the state specified in the lock file. This will not re-negotiate dependencies, as 'helm dependency update' @@ -50,7 +50,7 @@ func newDependencyBuildCmd(out io.Writer) *cobra.Command { cmd := &cobra.Command{ Use: "build CHART", - Short: "rebuild the charts/ directory based on the requirements.lock file", + Short: "rebuild the charts/ directory based on the Chart.lock file", Long: dependencyBuildDesc, Args: require.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { diff --git a/cmd/helm/dependency_build_test.go b/cmd/helm/dependency_build_test.go index 234fc9b30..5c2f004fb 100644 --- a/cmd/helm/dependency_build_test.go +++ b/cmd/helm/dependency_build_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 @@ -64,7 +64,7 @@ func TestDependencyBuildCmd(t *testing.T) { // In the second pass, we want to remove the chart's request dependency, // then see if it restores from the lock. - lockfile := hh.Path(chartname, "requirements.lock") + lockfile := hh.Path(chartname, "Chart.lock") if _, err := os.Stat(lockfile); err != nil { t.Fatal(err) } diff --git a/cmd/helm/dependency_test.go b/cmd/helm/dependency_test.go index 98dcab1a6..da4829736 100644 --- a/cmd/helm/dependency_test.go +++ b/cmd/helm/dependency_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/cmd/helm/dependency_update.go b/cmd/helm/dependency_update.go index 3a8f7c0f2..586c2e09b 100644 --- a/cmd/helm/dependency_update.go +++ b/cmd/helm/dependency_update.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 @@ -28,18 +28,18 @@ import ( ) const dependencyUpDesc = ` -Update the on-disk dependencies to mirror the requirements.yaml file. +Update the on-disk dependencies to mirror Chart.yaml. -This command verifies that the required charts, as expressed in 'requirements.yaml', +This command verifies that the required charts, as expressed in 'Chart.yaml', are present in 'charts/' and are at an acceptable version. It will pull down the latest charts that satisfy the dependencies, and clean up old dependencies. On successful update, this will generate a lock file that can be used to rebuild the requirements to an exact version. -Dependencies are not required to be represented in 'requirements.yaml'. For that +Dependencies are not required to be represented in 'Chart.yaml'. For that reason, an update command will not remove charts unless they are (a) present -in the requirements.yaml file, but (b) at the wrong version. +in the Chart.yaml file, but (b) at the wrong version. ` // dependencyUpdateOptions describes a 'helm dependency update' @@ -63,7 +63,7 @@ func newDependencyUpdateCmd(out io.Writer) *cobra.Command { cmd := &cobra.Command{ Use: "update CHART", Aliases: []string{"up"}, - Short: "update charts/ based on the contents of requirements.yaml", + Short: "update charts/ based on the contents of Chart.yaml", Long: dependencyUpDesc, Args: require.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { diff --git a/cmd/helm/dependency_update_test.go b/cmd/helm/dependency_update_test.go index 212106af4..f6a8b91cb 100644 --- a/cmd/helm/dependency_update_test.go +++ b/cmd/helm/dependency_update_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 @@ -24,11 +24,8 @@ import ( "strings" "testing" - "github.com/ghodss/yaml" - + "k8s.io/helm/pkg/chart" "k8s.io/helm/pkg/chartutil" - "k8s.io/helm/pkg/hapi/chart" - "k8s.io/helm/pkg/helm/helmpath" "k8s.io/helm/pkg/provenance" "k8s.io/helm/pkg/repo" "k8s.io/helm/pkg/repo/repotest" @@ -50,7 +47,8 @@ func TestDependencyUpdateCmd(t *testing.T) { t.Logf("Listening on directory %s", srv.Root()) chartname := "depup" - if err := createTestingChart(hh.String(), chartname, srv.URL()); err != nil { + md := createTestingMetadata(chartname, srv.URL()) + if _, err := chartutil.Create(md, hh.String()); err != nil { t.Fatal(err) } @@ -88,14 +86,12 @@ func TestDependencyUpdateCmd(t *testing.T) { // Now change the dependencies and update. This verifies that on update, // old dependencies are cleansed and new dependencies are added. - reqfile := &chartutil.Requirements{ - Dependencies: []*chartutil.Dependency{ - {Name: "reqtest", Version: "0.1.0", Repository: srv.URL()}, - {Name: "compressedchart", Version: "0.3.0", Repository: srv.URL()}, - }, + md.Requirements = []*chart.Dependency{ + {Name: "reqtest", Version: "0.1.0", Repository: srv.URL()}, + {Name: "compressedchart", Version: "0.3.0", Repository: srv.URL()}, } - dir := hh.Path(chartname) - if err := writeRequirements(dir, reqfile); err != nil { + dir := hh.Path(chartname, "Chart.yaml") + if err := chartutil.SaveChartfile(dir, md); err != nil { t.Fatal(err) } @@ -170,7 +166,7 @@ func TestDependencyUpdateCmd_DontDeleteOldChartsOnError(t *testing.T) { out := bytes.NewBuffer(nil) o := &dependencyUpdateOptions{} - o.helmhome = helmpath.Home(hh) + o.helmhome = hh o.chartpath = hh.Path(chartname) if err := o.run(out); err != nil { @@ -210,33 +206,25 @@ func TestDependencyUpdateCmd_DontDeleteOldChartsOnError(t *testing.T) { } } -// createTestingChart creates a basic chart that depends on reqtest-0.1.0 +// createTestingMetadata creates a basic chart that depends on reqtest-0.1.0 // // The baseURL can be used to point to a particular repository server. -func createTestingChart(dest, name, baseURL string) error { - cfile := &chart.Metadata{ +func createTestingMetadata(name, baseURL string) *chart.Metadata { + return &chart.Metadata{ Name: name, Version: "1.2.3", - } - dir := filepath.Join(dest, name) - _, err := chartutil.Create(cfile, dest) - if err != nil { - return err - } - req := &chartutil.Requirements{ - Dependencies: []*chartutil.Dependency{ + Requirements: []*chart.Dependency{ {Name: "reqtest", Version: "0.1.0", Repository: baseURL}, {Name: "compressedchart", Version: "0.1.0", Repository: baseURL}, }, } - return writeRequirements(dir, req) } -func writeRequirements(dir string, req *chartutil.Requirements) error { - data, err := yaml.Marshal(req) - if err != nil { - return err - } - - return ioutil.WriteFile(filepath.Join(dir, "requirements.yaml"), data, 0655) +// createTestingChart creates a basic chart that depends on reqtest-0.1.0 +// +// The baseURL can be used to point to a particular repository server. +func createTestingChart(dest, name, baseURL string) error { + cfile := createTestingMetadata(name, baseURL) + _, err := chartutil.Create(cfile, dest) + return err } diff --git a/cmd/helm/docs.go b/cmd/helm/docs.go index 1292e5807..fb219d490 100644 --- a/cmd/helm/docs.go +++ b/cmd/helm/docs.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/cmd/helm/get.go b/cmd/helm/get.go index ff9e9a3ca..cf826a747 100644 --- a/cmd/helm/get.go +++ b/cmd/helm/get.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/get_hooks.go b/cmd/helm/get_hooks.go index 19afa33cc..f455f4914 100644 --- a/cmd/helm/get_hooks.go +++ b/cmd/helm/get_hooks.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/get_hooks_test.go b/cmd/helm/get_hooks_test.go index 001c826b6..8ab99722d 100644 --- a/cmd/helm/get_hooks_test.go +++ b/cmd/helm/get_hooks_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/get_manifest.go b/cmd/helm/get_manifest.go index 136b8b581..7e0f43a56 100644 --- a/cmd/helm/get_manifest.go +++ b/cmd/helm/get_manifest.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/get_manifest_test.go b/cmd/helm/get_manifest_test.go index da29aee1d..f3737d968 100644 --- a/cmd/helm/get_manifest_test.go +++ b/cmd/helm/get_manifest_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/get_test.go b/cmd/helm/get_test.go index 1df178aa8..e6943475b 100644 --- a/cmd/helm/get_test.go +++ b/cmd/helm/get_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/get_values.go b/cmd/helm/get_values.go index cf1e37343..837301e88 100644 --- a/cmd/helm/get_values.go +++ b/cmd/helm/get_values.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/get_values_test.go b/cmd/helm/get_values_test.go index a9e81dbce..2b14bdf4c 100644 --- a/cmd/helm/get_values_test.go +++ b/cmd/helm/get_values_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index 390aa328a..7471dbaf8 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -23,8 +23,8 @@ import ( "sync" // Import to initialize client auth plugins. + "k8s.io/cli-runtime/pkg/genericclioptions" _ "k8s.io/client-go/plugin/pkg/client/auth" - "k8s.io/client-go/tools/clientcmd" "k8s.io/helm/pkg/helm" "k8s.io/helm/pkg/helm/environment" @@ -34,7 +34,7 @@ import ( var ( settings environment.EnvSettings - config clientcmd.ClientConfig + config genericclioptions.RESTClientGetter configOnce sync.Once ) @@ -89,7 +89,7 @@ func newClient(allNamespaces bool) helm.Interface { ) } -func kubeConfig() clientcmd.ClientConfig { +func kubeConfig() genericclioptions.RESTClientGetter { configOnce.Do(func() { config = kube.GetConfig(settings.KubeConfig, settings.KubeContext, settings.Namespace) }) @@ -97,7 +97,7 @@ func kubeConfig() clientcmd.ClientConfig { } func getNamespace() string { - if ns, _, err := kubeConfig().Namespace(); err == nil { + if ns, _, err := kubeConfig().ToRawKubeConfigLoader().Namespace(); err == nil { return ns } return "default" diff --git a/cmd/helm/helm_test.go b/cmd/helm/helm_test.go index f1d7563af..9bc68a8be 100644 --- a/cmd/helm/helm_test.go +++ b/cmd/helm/helm_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -141,12 +141,10 @@ func ensureTestHome(t *testing.T, home helmpath.Home) { } } if r, err := repo.LoadRepositoriesFile(repoFile); err == repo.ErrRepoOutOfDate { - t.Log("Updating repository file format...") if err := r.WriteFile(repoFile, 0644); err != nil { t.Fatal(err) } } - t.Logf("$HELM_HOME has been configured at %s.\n", home) } // testHelmHome sets up a Helm Home in a temp dir. diff --git a/cmd/helm/history.go b/cmd/helm/history.go index 1139d9648..5b3094e2a 100644 --- a/cmd/helm/history.go +++ b/cmd/helm/history.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -27,7 +27,7 @@ import ( "github.com/spf13/cobra" "k8s.io/helm/cmd/helm/require" - "k8s.io/helm/pkg/hapi/chart" + "k8s.io/helm/pkg/chart" "k8s.io/helm/pkg/hapi/release" "k8s.io/helm/pkg/helm" ) @@ -167,5 +167,5 @@ func formatChartname(c *chart.Chart) string { // know how: https://github.com/kubernetes/helm/issues/1347 return "MISSING" } - return fmt.Sprintf("%s-%s", c.Metadata.Name, c.Metadata.Version) + return fmt.Sprintf("%s-%s", c.Name(), c.Metadata.Version) } diff --git a/cmd/helm/history_test.go b/cmd/helm/history_test.go index 3d7786e04..6c83f51b2 100644 --- a/cmd/helm/history_test.go +++ b/cmd/helm/history_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/home.go b/cmd/helm/home.go index 87eded594..24d0a40a2 100644 --- a/cmd/helm/home.go +++ b/cmd/helm/home.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/init.go b/cmd/helm/init.go index 391d1cf93..42d923e95 100644 --- a/cmd/helm/init.go +++ b/cmd/helm/init.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/init_test.go b/cmd/helm/init_test.go index 5f6724b28..781939800 100644 --- a/cmd/helm/init_test.go +++ b/cmd/helm/init_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/inspect.go b/cmd/helm/inspect.go index 4f8327898..5b4872492 100644 --- a/cmd/helm/inspect.go +++ b/cmd/helm/inspect.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -25,8 +25,8 @@ import ( "github.com/spf13/cobra" "k8s.io/helm/cmd/helm/require" - "k8s.io/helm/pkg/chartutil" - "k8s.io/helm/pkg/hapi/chart" + "k8s.io/helm/pkg/chart" + "k8s.io/helm/pkg/chart/loader" ) const inspectDesc = ` @@ -146,7 +146,7 @@ func newInspectCmd(out io.Writer) *cobra.Command { } func (i *inspectOptions) run(out io.Writer) error { - chrt, err := chartutil.Load(i.chartpath) + chrt, err := loader.Load(i.chartpath) if err != nil { return err } @@ -163,7 +163,11 @@ func (i *inspectOptions) run(out io.Writer) error { if i.output == all { fmt.Fprintln(out, "---") } - fmt.Fprintln(out, string(chrt.Values)) + b, err := yaml.Marshal(chrt.Values) + if err != nil { + return err + } + fmt.Fprintln(out, string(b)) } if i.output == readmeOnly || i.output == all { diff --git a/cmd/helm/inspect_test.go b/cmd/helm/inspect_test.go index 5241cb2cc..405102160 100644 --- a/cmd/helm/inspect_test.go +++ b/cmd/helm/inspect_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/install.go b/cmd/helm/install.go index e7910d57e..09b1bec51 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -20,18 +20,20 @@ import ( "bytes" "fmt" "io" + "path/filepath" "strings" "text/template" + "time" "github.com/Masterminds/sprig" "github.com/pkg/errors" "github.com/spf13/cobra" "k8s.io/helm/cmd/helm/require" - "k8s.io/helm/pkg/chartutil" + "k8s.io/helm/pkg/chart" + "k8s.io/helm/pkg/chart/loader" "k8s.io/helm/pkg/downloader" "k8s.io/helm/pkg/getter" - "k8s.io/helm/pkg/hapi/chart" "k8s.io/helm/pkg/hapi/release" "k8s.io/helm/pkg/helm" ) @@ -46,27 +48,27 @@ To override values in a chart, use either the '--values' flag and pass in a file or use the '--set' flag and pass configuration from the command line, to force a string value use '--set-string'. - $ helm install -f myvalues.yaml ./redis + $ helm install -f myvalues.yaml myredis ./redis or - $ helm install --set name=prod ./redis + $ helm install --set name=prod myredis ./redis or - $ helm install --set-string long_int=1234567890 ./redis + $ helm install --set-string long_int=1234567890 myredis ./redis You can specify the '--values'/'-f' flag multiple times. The priority will be given to the last (right-most) file specified. For example, if both myvalues.yaml and override.yaml contained a key called 'Test', the value set in override.yaml would take precedence: - $ helm install -f myvalues.yaml -f override.yaml ./redis + $ helm install -f myvalues.yaml -f override.yaml myredis ./redis You can specify the '--set' flag multiple times. The priority will be given to the last (right-most) set specified. For example, if both 'bar' and 'newbar' values are set for a key called 'foo', the 'newbar' value would take precedence: - $ helm install --set foo=bar --set foo=newbar ./redis + $ helm install --set foo=bar --set foo=newbar myredis ./redis To check the generated manifests of a release without installing the chart, @@ -99,7 +101,7 @@ charts in a repository, use 'helm search'. ` type installOptions struct { - name string // --name + name string // arg 0 dryRun bool // --dry-run disableHooks bool // --disable-hooks replace bool // --replace @@ -108,7 +110,8 @@ type installOptions struct { wait bool // --wait devel bool // --devel depUp bool // --dep-up - chartPath string // arg + chartPath string // arg 1 + generateName bool // --generate-name valuesOptions chartPathOptions @@ -120,10 +123,10 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command { o := &installOptions{client: c} cmd := &cobra.Command{ - Use: "install [CHART]", - Short: "install a chart archive", + Use: "install [NAME] [CHART]", + Short: "install a chart", Long: installDesc, - Args: require.ExactArgs(1), + Args: require.MinimumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { debug("Original chart version: %q", o.version) if o.version == "" && o.devel { @@ -131,7 +134,13 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command { o.version = ">0.0.0-0" } - cp, err := o.locateChart(args[0]) + name, chart, err := o.nameAndChart(args) + if err != nil { + return err + } + o.name = name // FIXME + + cp, err := o.locateChart(chart) if err != nil { return err } @@ -142,7 +151,7 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command { } f := cmd.Flags() - f.StringVarP(&o.name, "name", "", "", "release name. If unspecified, it will autogenerate one for you") + f.BoolVarP(&o.generateName, "generate-name", "g", false, "generate the name (and omit the NAME parameter)") f.BoolVar(&o.dryRun, "dry-run", false, "simulate an install") f.BoolVar(&o.disableHooks, "no-hooks", false, "prevent hooks from running during install") f.BoolVar(&o.replace, "replace", false, "re-use the given name, even if that name is already used. This is unsafe in production") @@ -157,6 +166,41 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command { return cmd } +// nameAndChart returns the name of the release and the chart that should be used. +// +// This will read the flags and handle name generation if necessary. +func (o *installOptions) nameAndChart(args []string) (string, string, error) { + flagsNotSet := func() error { + if o.generateName { + return errors.New("cannot set --generate-name and also specify a name") + } + if o.nameTemplate != "" { + return errors.New("cannot set --name-template and also specify a name") + } + return nil + } + if len(args) == 2 { + return args[0], args[1], flagsNotSet() + } + + if o.nameTemplate != "" { + newName, err := templateName(o.nameTemplate) + return newName, args[0], err + } + + if !o.generateName { + return "", args[0], errors.New("must either provide a name or specify --generate-name") + } + + base := filepath.Base(args[0]) + if base == "." || base == "" { + base = "chart" + } + newName := fmt.Sprintf("%s-%d", base, time.Now().Unix()) + + return newName, args[0], nil +} + func (o *installOptions) run(out io.Writer) error { debug("CHART PATH: %s\n", o.chartPath) @@ -167,7 +211,7 @@ func (o *installOptions) run(out io.Writer) error { // If template is specified, try to run the template. if o.nameTemplate != "" { - o.name, err = generateName(o.nameTemplate) + o.name, err = templateName(o.nameTemplate) if err != nil { return err } @@ -176,12 +220,12 @@ func (o *installOptions) run(out io.Writer) error { } // Check chart requirements to make sure all dependencies are present in /charts - chartRequested, err := chartutil.Load(o.chartPath) + chartRequested, err := loader.Load(o.chartPath) if err != nil { return err } - if req, err := chartutil.LoadRequirements(chartRequested); err == nil { + if req := chartRequested.Metadata.Requirements; req != nil { // If checkDependencies returns an error, we have unfulfilled dependencies. // As of Helm 2.4.0, this is treated as a stopping condition: // https://github.com/kubernetes/helm/issues/2209 @@ -203,8 +247,6 @@ func (o *installOptions) run(out io.Writer) error { } } - } else if err != chartutil.ErrRequirementsNotFound { - return errors.Wrap(err, "cannot load requirements") } rel, err := o.client.InstallReleaseFromChart( @@ -272,41 +314,33 @@ func (o *installOptions) printRelease(out io.Writer, rel *release.Release) { if rel == nil { return } - // TODO: Switch to text/template like everything else. fmt.Fprintf(out, "NAME: %s\n", rel.Name) if settings.Debug { printRelease(out, rel) } } -func generateName(nameTemplate string) (string, error) { +func templateName(nameTemplate string) (string, error) { t, err := template.New("name-template").Funcs(sprig.TxtFuncMap()).Parse(nameTemplate) if err != nil { return "", err } var b bytes.Buffer err = t.Execute(&b, nil) - if err != nil { - return "", err - } - return b.String(), nil + return b.String(), err } -func checkDependencies(ch *chart.Chart, reqs *chartutil.Requirements) error { - missing := []string{} +func checkDependencies(ch *chart.Chart, reqs []*chart.Dependency) error { + var missing []string - deps := ch.Dependencies - for _, r := range reqs.Dependencies { - found := false - for _, d := range deps { - if d.Metadata.Name == r.Name { - found = true - break +OUTER: + for _, r := range reqs { + for _, d := range ch.Dependencies() { + if d.Name() == r.Name { + continue OUTER } } - if !found { - missing = append(missing, r.Name) - } + missing = append(missing, r.Name) } if len(missing) > 0 { diff --git a/cmd/helm/install_test.go b/cmd/helm/install_test.go index 298edb5f2..c0d93a15c 100644 --- a/cmd/helm/install_test.go +++ b/cmd/helm/install_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -27,37 +27,37 @@ func TestInstall(t *testing.T) { // Install, base case { name: "basic install", - cmd: "install testdata/testcharts/alpine --name aeneas", + cmd: "install aeneas testdata/testcharts/alpine ", golden: "output/install.txt", }, // Install, no hooks { name: "install without hooks", - cmd: "install testdata/testcharts/alpine --name aeneas --no-hooks", + cmd: "install aeneas testdata/testcharts/alpine --no-hooks", golden: "output/install-no-hooks.txt", }, // Install, values from cli { name: "install with values", - cmd: "install testdata/testcharts/alpine --name virgil --set foo=bar", + cmd: "install virgil testdata/testcharts/alpine --set foo=bar", golden: "output/install-with-values.txt", }, // Install, values from cli via multiple --set { name: "install with multiple values", - cmd: "install testdata/testcharts/alpine --name virgil --set foo=bar --set bar=foo", + cmd: "install virgil testdata/testcharts/alpine --set foo=bar --set bar=foo", golden: "output/install-with-multiple-values.txt", }, // Install, values from yaml { name: "install with values file", - cmd: "install testdata/testcharts/alpine --name virgil -f testdata/testcharts/alpine/extra_values.yaml", + cmd: "install virgil testdata/testcharts/alpine -f testdata/testcharts/alpine/extra_values.yaml", golden: "output/install-with-values-file.txt", }, // Install, values from multiple yaml { name: "install with values", - cmd: "install testdata/testcharts/alpine --name virgil -f testdata/testcharts/alpine/extra_values.yaml -f testdata/testcharts/alpine/more_values.yaml", + cmd: "install virgil testdata/testcharts/alpine -f testdata/testcharts/alpine/extra_values.yaml -f testdata/testcharts/alpine/more_values.yaml", golden: "output/install-with-multiple-values-files.txt", }, // Install, no charts @@ -70,19 +70,19 @@ func TestInstall(t *testing.T) { // Install, re-use name { name: "install and replace release", - cmd: "install testdata/testcharts/alpine --name aeneas --replace", + cmd: "install aeneas testdata/testcharts/alpine --replace", golden: "output/install-and-replace.txt", }, // Install, with timeout { name: "install with a timeout", - cmd: "install testdata/testcharts/alpine --name foobar --timeout 120", + cmd: "install foobar testdata/testcharts/alpine --timeout 120", golden: "output/install-with-timeout.txt", }, // Install, with wait { name: "install with a wait", - cmd: "install testdata/testcharts/alpine --name apollo --wait", + cmd: "install apollo testdata/testcharts/alpine --wait", golden: "output/install-with-wait.txt", }, // Install, using the name-template @@ -94,28 +94,28 @@ func TestInstall(t *testing.T) { // Install, perform chart verification along the way. { name: "install with verification, missing provenance", - cmd: "install testdata/testcharts/compressedchart-0.1.0.tgz --verify --keyring testdata/helm-test-key.pub", + cmd: "install bogus testdata/testcharts/compressedchart-0.1.0.tgz --verify --keyring testdata/helm-test-key.pub", wantError: true, }, { name: "install with verification, directory instead of file", - cmd: "install testdata/testcharts/signtest --verify --keyring testdata/helm-test-key.pub", + cmd: "install bogus testdata/testcharts/signtest --verify --keyring testdata/helm-test-key.pub", wantError: true, }, { name: "install with verification, valid", - cmd: "install testdata/testcharts/signtest-0.1.0.tgz --verify --keyring testdata/helm-test-key.pub", + cmd: "install signtest testdata/testcharts/signtest-0.1.0.tgz --verify --keyring testdata/helm-test-key.pub", }, // Install, chart with missing dependencies in /charts { name: "install chart with missing dependencies", - cmd: "install testdata/testcharts/chart-missing-deps", + cmd: "install nodeps testdata/testcharts/chart-missing-deps", wantError: true, }, - // Install, chart with bad requirements.yaml in /charts + // Install, chart with bad dependencies in Chart.yaml in /charts { - name: "install chart with bad requirements.yaml", - cmd: "install testdata/testcharts/chart-bad-requirements", + name: "install chart with bad dependencies in Chart.yaml", + cmd: "install badreq testdata/testcharts/chart-bad-requirements", wantError: true, }, } @@ -165,7 +165,7 @@ func TestNameTemplate(t *testing.T) { for _, tc := range testCases { - n, err := generateName(tc.tpl) + n, err := templateName(tc.tpl) if err != nil { if tc.expectedErrorStr == "" { t.Errorf("Was not expecting error, but got: %v", err) diff --git a/cmd/helm/lint.go b/cmd/helm/lint.go index 5483e61c0..dd71677b5 100644 --- a/cmd/helm/lint.go +++ b/cmd/helm/lint.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/lint_test.go b/cmd/helm/lint_test.go index d4fca66a8..1609debbe 100644 --- a/cmd/helm/lint_test.go +++ b/cmd/helm/lint_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/list.go b/cmd/helm/list.go index f159d207f..1285412e4 100644 --- a/cmd/helm/list.go +++ b/cmd/helm/list.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -34,8 +34,8 @@ var listHelp = ` This command lists all of the releases. By default, it lists only releases that are deployed or failed. Flags like -'--deleted' and '--all' will alter this behavior. Such flags can be combined: -'--deleted --failed'. +'--uninstalled' and '--all' will alter this behavior. Such flags can be combined: +'--uninstalled --failed'. By default, items are sorted alphabetically. Use the '-d' flag to sort by release date. @@ -63,8 +63,8 @@ type listOptions struct { allNamespaces bool // --all-namespaces byDate bool // --date colWidth uint // --col-width - deleted bool // --deleted - deleting bool // --deleting + uninstalled bool // --uninstalled + uninstalling bool // --uninstalling deployed bool // --deployed failed bool // --failed limit int // --max @@ -104,9 +104,9 @@ func newListCmd(client helm.Interface, out io.Writer) *cobra.Command { f.IntVarP(&o.limit, "max", "m", 256, "maximum number of releases to fetch") f.StringVarP(&o.offset, "offset", "o", "", "next release name in the list, used to offset from start value") f.BoolVarP(&o.all, "all", "a", false, "show all releases, not just the ones marked deployed") - f.BoolVar(&o.deleted, "deleted", false, "show deleted releases") + f.BoolVar(&o.uninstalled, "uninstalled", false, "show uninstalled releases") f.BoolVar(&o.superseded, "superseded", false, "show superseded releases") - f.BoolVar(&o.deleting, "deleting", false, "show releases that are currently being deleted") + f.BoolVar(&o.uninstalling, "uninstalling", false, "show releases that are currently being uninstalled") f.BoolVar(&o.deployed, "deployed", false, "show deployed releases. If no other is specified, this will be automatically enabled") f.BoolVar(&o.failed, "failed", false, "show failed releases") f.BoolVar(&o.pending, "pending", false, "show pending releases") @@ -188,8 +188,8 @@ func (o *listOptions) statusCodes() []release.ReleaseStatus { return []release.ReleaseStatus{ release.StatusUnknown, release.StatusDeployed, - release.StatusDeleted, - release.StatusDeleting, + release.StatusUninstalled, + release.StatusUninstalling, release.StatusFailed, release.StatusPendingInstall, release.StatusPendingUpgrade, @@ -200,11 +200,11 @@ func (o *listOptions) statusCodes() []release.ReleaseStatus { if o.deployed { status = append(status, release.StatusDeployed) } - if o.deleted { - status = append(status, release.StatusDeleted) + if o.uninstalled { + status = append(status, release.StatusUninstalled) } - if o.deleting { - status = append(status, release.StatusDeleting) + if o.uninstalling { + status = append(status, release.StatusUninstalling) } if o.failed { status = append(status, release.StatusFailed) diff --git a/cmd/helm/list_test.go b/cmd/helm/list_test.go index 2a331eb01..3561e6b1c 100644 --- a/cmd/helm/list_test.go +++ b/cmd/helm/list_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -48,9 +48,9 @@ func TestListCmd(t *testing.T) { golden: "output/list-with-failed.txt", }, { name: "with a release, multiple flags", - cmd: "list --deleted --deployed --failed -q", + cmd: "list --uninstalled --deployed --failed -q", rels: []*release.Release{ - helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide", Status: release.StatusDeleted}), + helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide", Status: release.StatusUninstalled}), helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas-guide", Status: release.StatusDeployed}), }, // Note: We're really only testing that the flags parsed correctly. Which results are returned @@ -60,7 +60,7 @@ func TestListCmd(t *testing.T) { name: "with a release, multiple flags", cmd: "list --all -q", rels: []*release.Release{ - helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide", Status: release.StatusDeleted}), + helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide", Status: release.StatusUninstalled}), helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas-guide", Status: release.StatusDeployed}), }, // See note on previous test. @@ -69,7 +69,7 @@ func TestListCmd(t *testing.T) { name: "with a release, multiple flags, deleting", cmd: "list --all -q", rels: []*release.Release{ - helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide", Status: release.StatusDeleting}), + helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide", Status: release.StatusUninstalling}), helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas-guide", Status: release.StatusDeployed}), }, // See note on previous test. diff --git a/cmd/helm/load_plugins.go b/cmd/helm/load_plugins.go index 129ffcb1b..124ebb5c1 100644 --- a/cmd/helm/load_plugins.go +++ b/cmd/helm/load_plugins.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/cmd/helm/options.go b/cmd/helm/options.go index 0a6a9ff8c..33dda1a79 100644 --- a/cmd/helm/options.go +++ b/cmd/helm/options.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/package.go b/cmd/helm/package.go index a41ee4f12..0178af6ab 100644 --- a/cmd/helm/package.go +++ b/cmd/helm/package.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -25,15 +25,15 @@ import ( "syscall" "github.com/Masterminds/semver" - "github.com/ghodss/yaml" "github.com/pkg/errors" "github.com/spf13/cobra" "golang.org/x/crypto/ssh/terminal" + "k8s.io/helm/pkg/chart" + "k8s.io/helm/pkg/chart/loader" "k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/downloader" "k8s.io/helm/pkg/getter" - "k8s.io/helm/pkg/hapi/chart" "k8s.io/helm/pkg/helm/helmpath" "k8s.io/helm/pkg/provenance" ) @@ -102,7 +102,7 @@ func newPackageCmd(out io.Writer) *cobra.Command { f.StringVar(&o.version, "version", "", "set the version on the chart to this semver version") f.StringVar(&o.appVersion, "app-version", "", "set the appVersion on the chart to this version") f.StringVarP(&o.destination, "destination", "d", ".", "location to write the chart.") - f.BoolVarP(&o.dependencyUpdate, "dependency-update", "u", false, `update dependencies from "requirements.yaml" to dir "charts/" before packaging`) + f.BoolVarP(&o.dependencyUpdate, "dependency-update", "u", false, `update dependencies from "Chart.yaml" to dir "charts/" before packaging`) o.valuesOptions.addFlags(f) return cmd @@ -129,7 +129,7 @@ func (o *packageOptions) run(out io.Writer) error { } } - ch, err := chartutil.LoadDir(path) + ch, err := loader.LoadDir(path) if err != nil { return err } @@ -142,11 +142,7 @@ func (o *packageOptions) run(out io.Writer) error { if err != nil { return err } - newVals, err := yaml.Marshal(combinedVals) - if err != nil { - return err - } - ch.Values = newVals + ch.Values = combinedVals // If version is set, modify the version. if len(o.version) != 0 { @@ -161,18 +157,10 @@ func (o *packageOptions) run(out io.Writer) error { debug("Setting appVersion to %s", o.appVersion) } - if filepath.Base(path) != ch.Metadata.Name { - return errors.Errorf("directory name (%s) and Chart.yaml name (%s) must match", filepath.Base(path), ch.Metadata.Name) - } - - if reqs, err := chartutil.LoadRequirements(ch); err == nil { + if reqs := ch.Metadata.Requirements; reqs != nil { if err := checkDependencies(ch, reqs); err != nil { return err } - } else { - if err != chartutil.ErrRequirementsNotFound { - return err - } } var dest string @@ -188,11 +176,10 @@ func (o *packageOptions) run(out io.Writer) error { } name, err := chartutil.Save(ch, dest) - if err == nil { - fmt.Fprintf(out, "Successfully packaged chart and saved it to: %s\n", name) - } else { + if err != nil { return errors.Wrap(err, "failed to save") } + fmt.Fprintf(out, "Successfully packaged chart and saved it to: %s\n", name) if o.sign { err = o.clearsign(name) diff --git a/cmd/helm/package_test.go b/cmd/helm/package_test.go index d7f0e0f9d..fb032bc4b 100644 --- a/cmd/helm/package_test.go +++ b/cmd/helm/package_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 @@ -26,8 +26,9 @@ import ( "github.com/spf13/cobra" + "k8s.io/helm/pkg/chart" + "k8s.io/helm/pkg/chart/loader" "k8s.io/helm/pkg/chartutil" - "k8s.io/helm/pkg/hapi/chart" "k8s.io/helm/pkg/helm/helmpath" ) @@ -97,6 +98,12 @@ func TestPackage(t *testing.T) { expect: "", hasfile: "alpine-0.1.0.tgz", }, + { + name: "package testdata/testcharts/issue1979", + args: []string{"testdata/testcharts/issue1979"}, + expect: "", + hasfile: "alpine-0.1.0.tgz", + }, { name: "package --destination toot", args: []string{"testdata/testcharts/alpine"}, @@ -206,7 +213,7 @@ func TestSetAppVersion(t *testing.T) { tmp := testTempDir(t) hh := testHelmHome(t) - settings.Home = helmpath.Home(hh) + settings.Home = hh c := newPackageCmd(&bytes.Buffer{}) flags := map[string]string{ @@ -224,7 +231,7 @@ func TestSetAppVersion(t *testing.T) { } else if fi.Size() == 0 { t.Errorf("file %q has zero bytes.", chartPath) } - ch, err := chartutil.Load(chartPath) + ch, err := loader.Load(chartPath) if err != nil { t.Errorf("unexpected error loading packaged chart: %v", err) } @@ -291,6 +298,7 @@ func TestPackageValues(t *testing.T) { } func runAndVerifyPackageCommandValues(t *testing.T, args []string, flags map[string]string, valueFiles string, expected chartutil.Values) { + t.Helper() outputDir := testTempDir(t) if len(flags) == 0 { @@ -332,15 +340,16 @@ func createValuesFile(t *testing.T, data string) string { func getChartValues(chartPath string) (chartutil.Values, error) { - chart, err := chartutil.Load(chartPath) + chart, err := loader.Load(chartPath) if err != nil { return nil, err } - return chartutil.ReadValues(chart.Values) + return chart.Values, nil } func verifyValues(t *testing.T, actual, expected chartutil.Values) { + t.Helper() for key, value := range expected.AsMap() { if got := actual[key]; got != value { t.Errorf("Expected %q, got %q (%v)", value, got, actual) diff --git a/cmd/helm/plugin.go b/cmd/helm/plugin.go index 16b859fcb..b41ec6f45 100644 --- a/cmd/helm/plugin.go +++ b/cmd/helm/plugin.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/cmd/helm/plugin_install.go b/cmd/helm/plugin_install.go index 74937fcce..0a255eecf 100644 --- a/cmd/helm/plugin_install.go +++ b/cmd/helm/plugin_install.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/cmd/helm/plugin_list.go b/cmd/helm/plugin_list.go index 367605f61..31a8b57b0 100644 --- a/cmd/helm/plugin_list.go +++ b/cmd/helm/plugin_list.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/cmd/helm/plugin_remove.go b/cmd/helm/plugin_remove.go index 104b5fda8..a0ff78ceb 100644 --- a/cmd/helm/plugin_remove.go +++ b/cmd/helm/plugin_remove.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/cmd/helm/plugin_test.go b/cmd/helm/plugin_test.go index 32af2cd21..537ca1ce1 100644 --- a/cmd/helm/plugin_test.go +++ b/cmd/helm/plugin_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/cmd/helm/plugin_update.go b/cmd/helm/plugin_update.go index 67817de8e..a84312eb0 100644 --- a/cmd/helm/plugin_update.go +++ b/cmd/helm/plugin_update.go @@ -1,5 +1,5 @@ /* -Copyright 2017 The Kubernetes Authors All rights reserved. +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 diff --git a/cmd/helm/printer.go b/cmd/helm/printer.go index 318832d09..aa20590ca 100644 --- a/cmd/helm/printer.go +++ b/cmd/helm/printer.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/fetch.go b/cmd/helm/pull.go similarity index 90% rename from cmd/helm/fetch.go rename to cmd/helm/pull.go index 0a4ff2635..570a69ed0 100644 --- a/cmd/helm/fetch.go +++ b/cmd/helm/pull.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -33,7 +33,7 @@ import ( "k8s.io/helm/pkg/repo" ) -const fetchDesc = ` +const pullDesc = ` Retrieve a package from a package repository, and download it locally. This is useful for fetching packages to inspect, modify, or repackage. It can @@ -48,7 +48,7 @@ file, and MUST pass the verification process. Failure in any part of this will result in an error, and the chart will not be saved locally. ` -type fetchOptions struct { +type pullOptions struct { destdir string // --destination devel bool // --devel untar bool // --untar @@ -60,14 +60,15 @@ type fetchOptions struct { chartPathOptions } -func newFetchCmd(out io.Writer) *cobra.Command { - o := &fetchOptions{} +func newPullCmd(out io.Writer) *cobra.Command { + o := &pullOptions{} cmd := &cobra.Command{ - Use: "fetch [chart URL | repo/chartname] [...]", - Short: "download a chart from a repository and (optionally) unpack it in local directory", - Long: fetchDesc, - Args: require.MinimumNArgs(1), + Use: "pull [chart URL | repo/chartname] [...]", + Short: "download a chart from a repository and (optionally) unpack it in local directory", + Aliases: []string{"fetch"}, + Long: pullDesc, + Args: require.MinimumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { if o.version == "" && o.devel { debug("setting version to >0.0.0-0") @@ -96,7 +97,7 @@ func newFetchCmd(out io.Writer) *cobra.Command { return cmd } -func (o *fetchOptions) run(out io.Writer) error { +func (o *pullOptions) run(out io.Writer) error { c := downloader.ChartDownloader{ HelmHome: settings.Home, Out: out, diff --git a/cmd/helm/fetch_test.go b/cmd/helm/pull_test.go similarity index 97% rename from cmd/helm/fetch_test.go rename to cmd/helm/pull_test.go index 86c0c5327..cd09b26cb 100644 --- a/cmd/helm/fetch_test.go +++ b/cmd/helm/pull_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -27,7 +27,7 @@ import ( "k8s.io/helm/pkg/repo/repotest" ) -func TestFetchCmd(t *testing.T) { +func TestPullCmd(t *testing.T) { defer resetEnv()() hh := testHelmHome(t) diff --git a/cmd/helm/release_testing.go b/cmd/helm/release_testing.go index bd265f123..08d3b0383 100644 --- a/cmd/helm/release_testing.go +++ b/cmd/helm/release_testing.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/release_testing_test.go b/cmd/helm/release_testing_test.go index 4f7392b93..28be860b4 100644 --- a/cmd/helm/release_testing_test.go +++ b/cmd/helm/release_testing_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/repo.go b/cmd/helm/repo.go index a8166cc44..1868ba4dd 100644 --- a/cmd/helm/repo.go +++ b/cmd/helm/repo.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/repo_add.go b/cmd/helm/repo_add.go index 62427a69f..6159525f8 100644 --- a/cmd/helm/repo_add.go +++ b/cmd/helm/repo_add.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/repo_add_test.go b/cmd/helm/repo_add_test.go index 406d9c756..896b4f946 100644 --- a/cmd/helm/repo_add_test.go +++ b/cmd/helm/repo_add_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/repo_index.go b/cmd/helm/repo_index.go index f578a80c7..e7ebbce23 100644 --- a/cmd/helm/repo_index.go +++ b/cmd/helm/repo_index.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/repo_index_test.go b/cmd/helm/repo_index_test.go index 026e162f3..7b2e8e275 100644 --- a/cmd/helm/repo_index_test.go +++ b/cmd/helm/repo_index_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/repo_list.go b/cmd/helm/repo_list.go index 4d5dc546a..b79e0ba94 100644 --- a/cmd/helm/repo_list.go +++ b/cmd/helm/repo_list.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/repo_remove.go b/cmd/helm/repo_remove.go index 91b9b1fa9..59ccbe57b 100644 --- a/cmd/helm/repo_remove.go +++ b/cmd/helm/repo_remove.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/repo_remove_test.go b/cmd/helm/repo_remove_test.go index 340af3ef8..c274464a5 100644 --- a/cmd/helm/repo_remove_test.go +++ b/cmd/helm/repo_remove_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/repo_update.go b/cmd/helm/repo_update.go index 15d3505c9..d8f6a9012 100644 --- a/cmd/helm/repo_update.go +++ b/cmd/helm/repo_update.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/repo_update_test.go b/cmd/helm/repo_update_test.go index b84cd7a2d..d7001f31c 100644 --- a/cmd/helm/repo_update_test.go +++ b/cmd/helm/repo_update_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 @@ -45,7 +45,7 @@ func TestUpdateCmd(t *testing.T) { } o := &repoUpdateOptions{ update: updater, - home: helmpath.Home(hh), + home: hh, } if err := o.run(out); err != nil { t.Fatal(err) diff --git a/cmd/helm/require/args.go b/cmd/helm/require/args.go index 3c71d4b7b..cfa8a0169 100644 --- a/cmd/helm/require/args.go +++ b/cmd/helm/require/args.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors All rights reserved. +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 diff --git a/cmd/helm/require/args_test.go b/cmd/helm/require/args_test.go index 4098ed314..c8d5c3110 100644 --- a/cmd/helm/require/args_test.go +++ b/cmd/helm/require/args_test.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors All rights reserved. +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 diff --git a/cmd/helm/rollback.go b/cmd/helm/rollback.go index 29e025cc7..521141e11 100644 --- a/cmd/helm/rollback.go +++ b/cmd/helm/rollback.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/rollback_test.go b/cmd/helm/rollback_test.go index 73624c31e..862f7521b 100644 --- a/cmd/helm/rollback_test.go +++ b/cmd/helm/rollback_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/root.go b/cmd/helm/root.go index 15737a6e9..96d37173a 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -62,7 +62,7 @@ func newRootCmd(c helm.Interface, out io.Writer, args []string) *cobra.Command { // chart commands newCreateCmd(out), newDependencyCmd(out), - newFetchCmd(out), + newPullCmd(out), newInspectCmd(out), newLintCmd(out), newPackageCmd(out), @@ -71,7 +71,6 @@ func newRootCmd(c helm.Interface, out io.Writer, args []string) *cobra.Command { newVerifyCmd(out), // release commands - newDeleteCmd(c, out), newGetCmd(c, out), newHistoryCmd(c, out), newInstallCmd(c, out), @@ -79,6 +78,7 @@ func newRootCmd(c helm.Interface, out io.Writer, args []string) *cobra.Command { newReleaseTestCmd(c, out), newRollbackCmd(c, out), newStatusCmd(c, out), + newUninstallCmd(c, out), newUpgradeCmd(c, out), newCompletionCmd(out), diff --git a/cmd/helm/root_test.go b/cmd/helm/root_test.go index 4787a43b1..bd41b6ab6 100644 --- a/cmd/helm/root_test.go +++ b/cmd/helm/root_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/search.go b/cmd/helm/search.go index 10c895b4d..704d79511 100644 --- a/cmd/helm/search.go +++ b/cmd/helm/search.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/search/search.go b/cmd/helm/search/search.go index baccdaf56..dfc640ccc 100644 --- a/cmd/helm/search/search.go +++ b/cmd/helm/search/search.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/search/search_test.go b/cmd/helm/search/search_test.go index 19568d9ca..69fb1b82e 100644 --- a/cmd/helm/search/search_test.go +++ b/cmd/helm/search/search_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -20,7 +20,7 @@ import ( "strings" "testing" - "k8s.io/helm/pkg/hapi/chart" + "k8s.io/helm/pkg/chart" "k8s.io/helm/pkg/repo" ) diff --git a/cmd/helm/search_test.go b/cmd/helm/search_test.go index 9733942c0..380f87d34 100644 --- a/cmd/helm/search_test.go +++ b/cmd/helm/search_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/status.go b/cmd/helm/status.go index 6894f0bbd..ca0dab23f 100644 --- a/cmd/helm/status.go +++ b/cmd/helm/status.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/status_test.go b/cmd/helm/status_test.go index 787b59e6f..8b7f41cb6 100644 --- a/cmd/helm/status_test.go +++ b/cmd/helm/status_test.go @@ -1,5 +1,5 @@ /* -Copyright 2017 The Kubernetes Authors All rights reserved. +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. @@ -25,7 +25,7 @@ import ( func TestStatusCmd(t *testing.T) { releasesMockWithStatus := func(info *release.Info) []*release.Release { - info.LastDeployed = time.Unix(1452902400, 0) + info.LastDeployed = time.Unix(1452902400, 0).UTC() return []*release.Release{{ Name: "flummoxed-chickadee", Info: info, diff --git a/cmd/helm/template.go b/cmd/helm/template.go index a69cd6c6f..4c48f0e6d 100644 --- a/cmd/helm/template.go +++ b/cmd/helm/template.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -27,16 +27,17 @@ import ( "time" "github.com/Masterminds/semver" + "github.com/ghodss/yaml" "github.com/pkg/errors" "github.com/spf13/cobra" "k8s.io/helm/cmd/helm/require" + "k8s.io/helm/pkg/chart/loader" "k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/engine" "k8s.io/helm/pkg/hapi/release" util "k8s.io/helm/pkg/releaseutil" "k8s.io/helm/pkg/tiller" - tversion "k8s.io/helm/pkg/version" ) const defaultDirectoryPermission = 0755 @@ -145,30 +146,32 @@ func (o *templateOptions) run(out io.Writer) error { // If template is specified, try to run the template. if o.nameTemplate != "" { - o.releaseName, err = generateName(o.nameTemplate) + o.releaseName, err = templateName(o.nameTemplate) if err != nil { return err } } // Check chart requirements to make sure all dependencies are present in /charts - c, err := chartutil.Load(o.chartPath) + c, err := loader.Load(o.chartPath) if err != nil { return err } - if req, err := chartutil.LoadRequirements(c); err == nil { + if req := c.Metadata.Requirements; req != nil { if err := checkDependencies(c, req); err != nil { return err } - } else if err != chartutil.ErrRequirementsNotFound { - return errors.Wrap(err, "cannot load requirements") } options := chartutil.ReleaseOptions{ Name: o.releaseName, } - if err := chartutil.ProcessRequirementsEnabled(c, config); err != nil { + var m map[string]interface{} + if err := yaml.Unmarshal(config, &m); err != nil { + return err + } + if err := chartutil.ProcessRequirementsEnabled(c, m); err != nil { return err } if err := chartutil.ProcessRequirementsImportValues(c); err != nil { @@ -178,22 +181,18 @@ func (o *templateOptions) run(out io.Writer) error { // Set up engine. renderer := engine.New() - caps := &chartutil.Capabilities{ - APIVersions: chartutil.DefaultVersionSet, - KubeVersion: chartutil.DefaultKubeVersion, - HelmVersion: tversion.GetBuildInfo(), - } - // kubernetes version kv, err := semver.NewVersion(o.kubeVersion) if err != nil { return errors.Wrap(err, "could not parse a kubernetes version") } + + caps := chartutil.DefaultCapabilities caps.KubeVersion.Major = fmt.Sprint(kv.Major()) caps.KubeVersion.Minor = fmt.Sprint(kv.Minor()) caps.KubeVersion.GitVersion = fmt.Sprintf("v%d.%d.0", kv.Major(), kv.Minor()) - vals, err := chartutil.ToRenderValuesCaps(c, config, options, caps) + vals, err := chartutil.ToRenderValues(c, config, options, caps) if err != nil { return err } diff --git a/cmd/helm/template_test.go b/cmd/helm/template_test.go index 9bc0d5de8..417030d1c 100644 --- a/cmd/helm/template_test.go +++ b/cmd/helm/template_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/testdata/output/delete-no-args.txt b/cmd/helm/testdata/output/delete-no-args.txt deleted file mode 100644 index f06a4cb4e..000000000 --- a/cmd/helm/testdata/output/delete-no-args.txt +++ /dev/null @@ -1,3 +0,0 @@ -Error: "helm delete" requires at least 1 argument - -Usage: helm delete RELEASE_NAME [...] [flags] diff --git a/cmd/helm/testdata/output/delete-no-hooks.txt b/cmd/helm/testdata/output/delete-no-hooks.txt deleted file mode 100644 index 1292e5eb7..000000000 --- a/cmd/helm/testdata/output/delete-no-hooks.txt +++ /dev/null @@ -1 +0,0 @@ -release "aeneas" deleted diff --git a/cmd/helm/testdata/output/delete-purge.txt b/cmd/helm/testdata/output/delete-purge.txt deleted file mode 100644 index 1292e5eb7..000000000 --- a/cmd/helm/testdata/output/delete-purge.txt +++ /dev/null @@ -1 +0,0 @@ -release "aeneas" deleted diff --git a/cmd/helm/testdata/output/delete-timeout.txt b/cmd/helm/testdata/output/delete-timeout.txt deleted file mode 100644 index 1292e5eb7..000000000 --- a/cmd/helm/testdata/output/delete-timeout.txt +++ /dev/null @@ -1 +0,0 @@ -release "aeneas" deleted diff --git a/cmd/helm/testdata/output/delete.txt b/cmd/helm/testdata/output/delete.txt deleted file mode 100644 index 1292e5eb7..000000000 --- a/cmd/helm/testdata/output/delete.txt +++ /dev/null @@ -1 +0,0 @@ -release "aeneas" deleted diff --git a/cmd/helm/testdata/output/dependency-list-archive.txt b/cmd/helm/testdata/output/dependency-list-archive.txt index 098bc0635..a0fc13cd0 100644 --- a/cmd/helm/testdata/output/dependency-list-archive.txt +++ b/cmd/helm/testdata/output/dependency-list-archive.txt @@ -1,4 +1,5 @@ NAME VERSION REPOSITORY STATUS reqsubchart 0.1.0 https://example.com/charts missing reqsubchart2 0.2.0 https://example.com/charts missing +reqsubchart3 >=0.1.0 https://example.com/charts missing diff --git a/cmd/helm/testdata/output/install-no-args.txt b/cmd/helm/testdata/output/install-no-args.txt index faafcb5c2..47f010ab8 100644 --- a/cmd/helm/testdata/output/install-no-args.txt +++ b/cmd/helm/testdata/output/install-no-args.txt @@ -1,3 +1,3 @@ -Error: "helm install" requires 1 argument +Error: "helm install" requires at least 1 argument -Usage: helm install [CHART] [flags] +Usage: helm install [NAME] [CHART] [flags] diff --git a/cmd/helm/testdata/output/uninstall-no-args.txt b/cmd/helm/testdata/output/uninstall-no-args.txt new file mode 100644 index 000000000..fc01a75b9 --- /dev/null +++ b/cmd/helm/testdata/output/uninstall-no-args.txt @@ -0,0 +1,3 @@ +Error: "helm uninstall" requires at least 1 argument + +Usage: helm uninstall RELEASE_NAME [...] [flags] diff --git a/cmd/helm/testdata/output/uninstall-no-hooks.txt b/cmd/helm/testdata/output/uninstall-no-hooks.txt new file mode 100644 index 000000000..f5454b88d --- /dev/null +++ b/cmd/helm/testdata/output/uninstall-no-hooks.txt @@ -0,0 +1 @@ +release "aeneas" uninstalled diff --git a/cmd/helm/testdata/output/uninstall-purge.txt b/cmd/helm/testdata/output/uninstall-purge.txt new file mode 100644 index 000000000..f5454b88d --- /dev/null +++ b/cmd/helm/testdata/output/uninstall-purge.txt @@ -0,0 +1 @@ +release "aeneas" uninstalled diff --git a/cmd/helm/testdata/output/uninstall-timeout.txt b/cmd/helm/testdata/output/uninstall-timeout.txt new file mode 100644 index 000000000..f5454b88d --- /dev/null +++ b/cmd/helm/testdata/output/uninstall-timeout.txt @@ -0,0 +1 @@ +release "aeneas" uninstalled diff --git a/cmd/helm/testdata/output/uninstall.txt b/cmd/helm/testdata/output/uninstall.txt new file mode 100644 index 000000000..f5454b88d --- /dev/null +++ b/cmd/helm/testdata/output/uninstall.txt @@ -0,0 +1 @@ +release "aeneas" uninstalled diff --git a/cmd/helm/testdata/output/upgrade-with-bad-dependencies.txt b/cmd/helm/testdata/output/upgrade-with-bad-dependencies.txt index c77fba18b..5652097a6 100644 --- a/cmd/helm/testdata/output/upgrade-with-bad-dependencies.txt +++ b/cmd/helm/testdata/output/upgrade-with-bad-dependencies.txt @@ -1 +1 @@ -Error: cannot load requirements: error converting YAML to JSON: yaml: line 2: did not find expected '-' indicator +Error: cannot load Chart.yaml: error converting YAML to JSON: yaml: line 5: did not find expected '-' indicator diff --git a/cmd/helm/testdata/testcharts/alpine/values.yaml b/cmd/helm/testdata/testcharts/alpine/values.yaml index 879d760f9..807e12aea 100644 --- a/cmd/helm/testdata/testcharts/alpine/values.yaml +++ b/cmd/helm/testdata/testcharts/alpine/values.yaml @@ -1,2 +1 @@ -# The pod name Name: my-alpine diff --git a/cmd/helm/testdata/testcharts/chart-bad-requirements/Chart.yaml b/cmd/helm/testdata/testcharts/chart-bad-requirements/Chart.yaml index 02be4c013..ba72c77bf 100644 --- a/cmd/helm/testdata/testcharts/chart-bad-requirements/Chart.yaml +++ b/cmd/helm/testdata/testcharts/chart-bad-requirements/Chart.yaml @@ -1,3 +1,7 @@ description: A Helm chart for Kubernetes name: chart-missing-deps version: 0.1.0 +dependencies: + - name: reqsubchart + version: 0.1.0 + repository: "https://example.com/charts" diff --git a/cmd/helm/testdata/testcharts/chart-missing-deps/Chart.yaml b/cmd/helm/testdata/testcharts/chart-missing-deps/Chart.yaml index 02be4c013..8cd47d20c 100644 --- a/cmd/helm/testdata/testcharts/chart-missing-deps/Chart.yaml +++ b/cmd/helm/testdata/testcharts/chart-missing-deps/Chart.yaml @@ -1,3 +1,10 @@ description: A Helm chart for Kubernetes name: chart-missing-deps version: 0.1.0 +dependencies: + - name: reqsubchart + version: 0.1.0 + repository: "https://example.com/charts" + - name: reqsubchart2 + version: 0.2.0 + repository: "https://example.com/charts" diff --git a/cmd/helm/testdata/testcharts/issue1979/Chart.yaml b/cmd/helm/testdata/testcharts/issue1979/Chart.yaml new file mode 100644 index 000000000..6fbb27f18 --- /dev/null +++ b/cmd/helm/testdata/testcharts/issue1979/Chart.yaml @@ -0,0 +1,6 @@ +description: Deploy a basic Alpine Linux pod +home: https://k8s.io/helm +name: alpine +sources: +- https://github.com/kubernetes/helm +version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/issue1979/README.md b/cmd/helm/testdata/testcharts/issue1979/README.md new file mode 100644 index 000000000..3c32de5db --- /dev/null +++ b/cmd/helm/testdata/testcharts/issue1979/README.md @@ -0,0 +1,13 @@ +#Alpine: A simple Helm chart + +Run a single pod of Alpine Linux. + +This example was generated using the command `helm create alpine`. + +The `templates/` directory contains a very simple pod resource with a +couple of parameters. + +The `values.yaml` file contains the default values for the +`alpine-pod.yaml` template. + +You can install this example using `helm install docs/examples/alpine`. diff --git a/cmd/helm/testdata/testcharts/issue1979/extra_values.yaml b/cmd/helm/testdata/testcharts/issue1979/extra_values.yaml new file mode 100644 index 000000000..468bbacbc --- /dev/null +++ b/cmd/helm/testdata/testcharts/issue1979/extra_values.yaml @@ -0,0 +1,2 @@ +test: + Name: extra-values diff --git a/cmd/helm/testdata/testcharts/issue1979/more_values.yaml b/cmd/helm/testdata/testcharts/issue1979/more_values.yaml new file mode 100644 index 000000000..3d21e1fed --- /dev/null +++ b/cmd/helm/testdata/testcharts/issue1979/more_values.yaml @@ -0,0 +1,2 @@ +test: + Name: more-values diff --git a/cmd/helm/testdata/testcharts/issue1979/templates/alpine-pod.yaml b/cmd/helm/testdata/testcharts/issue1979/templates/alpine-pod.yaml new file mode 100644 index 000000000..ee61f2056 --- /dev/null +++ b/cmd/helm/testdata/testcharts/issue1979/templates/alpine-pod.yaml @@ -0,0 +1,25 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{.Release.Name}}-{{.Values.Name}}" + labels: + # The "heritage" label is used to track which tool deployed a given chart. + # It is useful for admins who want to see what releases a particular tool + # is responsible for. + heritage: {{.Release.Service | quote }} + # The "release" convention makes it easy to tie a release to all of the + # Kubernetes resources that were created as part of that release. + release: {{.Release.Name | quote }} + # This makes it easy to audit chart usage. + chart: "{{.Chart.Name}}-{{.Chart.Version}}" + values: {{.Values.test.Name}} +spec: + # This shows how to use a simple value. This will look for a passed-in value + # called restartPolicy. If it is not found, it will use the default value. + # {{default "Never" .restartPolicy}} is a slightly optimized version of the + # more conventional syntax: {{.restartPolicy | default "Never"}} + restartPolicy: {{default "Never" .Values.restartPolicy}} + containers: + - name: waiter + image: "alpine:3.3" + command: ["/bin/sleep","9000"] diff --git a/cmd/helm/testdata/testcharts/issue1979/values.yaml b/cmd/helm/testdata/testcharts/issue1979/values.yaml new file mode 100644 index 000000000..879d760f9 --- /dev/null +++ b/cmd/helm/testdata/testcharts/issue1979/values.yaml @@ -0,0 +1,2 @@ +# The pod name +Name: my-alpine diff --git a/cmd/helm/testdata/testcharts/reqtest-0.1.0.tgz b/cmd/helm/testdata/testcharts/reqtest-0.1.0.tgz index 356bc9303..8618e91d7 100644 Binary files a/cmd/helm/testdata/testcharts/reqtest-0.1.0.tgz and b/cmd/helm/testdata/testcharts/reqtest-0.1.0.tgz differ diff --git a/cmd/helm/testdata/testcharts/reqtest/requirements.lock b/cmd/helm/testdata/testcharts/reqtest/Chart.lock similarity index 100% rename from cmd/helm/testdata/testcharts/reqtest/requirements.lock rename to cmd/helm/testdata/testcharts/reqtest/Chart.lock diff --git a/cmd/helm/testdata/testcharts/reqtest/Chart.yaml b/cmd/helm/testdata/testcharts/reqtest/Chart.yaml index e2fbe4b01..e38826af3 100644 --- a/cmd/helm/testdata/testcharts/reqtest/Chart.yaml +++ b/cmd/helm/testdata/testcharts/reqtest/Chart.yaml @@ -1,3 +1,13 @@ description: A Helm chart for Kubernetes name: reqtest version: 0.1.0 +dependencies: + - name: reqsubchart + version: 0.1.0 + repository: "https://example.com/charts" + - name: reqsubchart2 + version: 0.2.0 + repository: "https://example.com/charts" + - name: reqsubchart3 + version: ">=0.1.0" + repository: "https://example.com/charts" diff --git a/cmd/helm/delete.go b/cmd/helm/uninstall.go similarity index 62% rename from cmd/helm/delete.go rename to cmd/helm/uninstall.go index eec08ccc1..863a62fbb 100644 --- a/cmd/helm/delete.go +++ b/cmd/helm/uninstall.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -26,15 +26,15 @@ import ( "k8s.io/helm/pkg/helm" ) -const deleteDesc = ` -This command takes a release name, and then deletes the release from Kubernetes. +const uninstallDesc = ` +This command takes a release name, and then uninstalls the release from Kubernetes. It removes all of the resources associated with the last release of the chart. -Use the '--dry-run' flag to see which releases will be deleted without actually -deleting them. +Use the '--dry-run' flag to see which releases will be uninstalled without actually +uninstalling them. ` -type deleteOptions struct { +type uninstallOptions struct { disableHooks bool // --no-hooks dryRun bool // --dry-run purge bool // --purge @@ -46,15 +46,15 @@ type deleteOptions struct { client helm.Interface } -func newDeleteCmd(c helm.Interface, out io.Writer) *cobra.Command { - o := &deleteOptions{client: c} +func newUninstallCmd(c helm.Interface, out io.Writer) *cobra.Command { + o := &uninstallOptions{client: c} cmd := &cobra.Command{ - Use: "delete RELEASE_NAME [...]", - Aliases: []string{"del"}, + Use: "uninstall RELEASE_NAME [...]", + Aliases: []string{"del", "delete", "un"}, SuggestFor: []string{"remove", "rm"}, - Short: "given a release name, delete the release from Kubernetes", - Long: deleteDesc, + Short: "given a release name, uninstall the release from Kubernetes", + Long: uninstallDesc, Args: require.MinimumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { o.client = ensureHelmClient(o.client, false) @@ -65,29 +65,29 @@ func newDeleteCmd(c helm.Interface, out io.Writer) *cobra.Command { return err } - fmt.Fprintf(out, "release \"%s\" deleted\n", o.name) + fmt.Fprintf(out, "release \"%s\" uninstalled\n", o.name) } return nil }, } f := cmd.Flags() - f.BoolVar(&o.dryRun, "dry-run", false, "simulate a delete") - f.BoolVar(&o.disableHooks, "no-hooks", false, "prevent hooks from running during deletion") + f.BoolVar(&o.dryRun, "dry-run", false, "simulate a uninstall") + f.BoolVar(&o.disableHooks, "no-hooks", false, "prevent hooks from running during uninstallation") f.BoolVar(&o.purge, "purge", false, "remove the release from the store and make its name free for later use") f.Int64Var(&o.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)") return cmd } -func (o *deleteOptions) run(out io.Writer) error { - opts := []helm.DeleteOption{ - helm.DeleteDryRun(o.dryRun), - helm.DeleteDisableHooks(o.disableHooks), - helm.DeletePurge(o.purge), - helm.DeleteTimeout(o.timeout), +func (o *uninstallOptions) run(out io.Writer) error { + opts := []helm.UninstallOption{ + helm.UninstallDryRun(o.dryRun), + helm.UninstallDisableHooks(o.disableHooks), + helm.UninstallPurge(o.purge), + helm.UninstallTimeout(o.timeout), } - res, err := o.client.DeleteRelease(o.name, opts...) + res, err := o.client.UninstallRelease(o.name, opts...) if res != nil && res.Info != "" { fmt.Fprintln(out, res.Info) } diff --git a/cmd/helm/delete_test.go b/cmd/helm/uninstall_test.go similarity index 60% rename from cmd/helm/delete_test.go rename to cmd/helm/uninstall_test.go index a391d995f..d83174479 100644 --- a/cmd/helm/delete_test.go +++ b/cmd/helm/uninstall_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -23,39 +23,39 @@ import ( "k8s.io/helm/pkg/helm" ) -func TestDelete(t *testing.T) { +func TestUninstall(t *testing.T) { rels := []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"})} tests := []cmdTestCase{ { - name: "basic delete", - cmd: "delete aeneas", - golden: "output/delete.txt", + name: "basic uninstall", + cmd: "uninstall aeneas", + golden: "output/uninstall.txt", rels: rels, }, { - name: "delete with timeout", - cmd: "delete aeneas --timeout 120", - golden: "output/delete-timeout.txt", + name: "uninstall with timeout", + cmd: "uninstall aeneas --timeout 120", + golden: "output/uninstall-timeout.txt", rels: rels, }, { - name: "delete without hooks", - cmd: "delete aeneas --no-hooks", - golden: "output/delete-no-hooks.txt", + name: "uninstall without hooks", + cmd: "uninstall aeneas --no-hooks", + golden: "output/uninstall-no-hooks.txt", rels: rels, }, { name: "purge", - cmd: "delete aeneas --purge", - golden: "output/delete-purge.txt", + cmd: "uninstall aeneas --purge", + golden: "output/uninstall-purge.txt", rels: rels, }, { - name: "delete without release", - cmd: "delete", - golden: "output/delete-no-args.txt", + name: "uninstall without release", + cmd: "uninstall", + golden: "output/uninstall-no-args.txt", wantError: true, }, } diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go index 83e2bb24d..30f1198a4 100644 --- a/cmd/helm/upgrade.go +++ b/cmd/helm/upgrade.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -19,13 +19,12 @@ package main import ( "fmt" "io" - "strings" "github.com/pkg/errors" "github.com/spf13/cobra" "k8s.io/helm/cmd/helm/require" - "k8s.io/helm/pkg/chartutil" + "k8s.io/helm/pkg/chart/loader" "k8s.io/helm/pkg/helm" "k8s.io/helm/pkg/storage/driver" ) @@ -125,9 +124,7 @@ func (o *upgradeOptions) run(out io.Writer) error { if o.install { // If a release does not exist, install it. If another error occurs during // the check, ignore the error and continue with the upgrade. - _, err := o.client.ReleaseHistory(o.release, 1) - - if err != nil && strings.Contains(err.Error(), driver.ErrReleaseNotFound(o.release).Error()) { + if _, err := o.client.ReleaseHistory(o.release, 1); err == driver.ErrReleaseNotFound { fmt.Fprintf(out, "Release %q does not exist. Installing it now.\n", o.release) io := &installOptions{ chartPath: chartPath, @@ -150,17 +147,15 @@ func (o *upgradeOptions) run(out io.Writer) error { } // Check chart requirements to make sure all dependencies are present in /charts - if ch, err := chartutil.Load(chartPath); err == nil { - if req, err := chartutil.LoadRequirements(ch); err == nil { - if err := checkDependencies(ch, req); err != nil { - return err - } - } else if err != chartutil.ErrRequirementsNotFound { - return errors.Wrap(err, "cannot load requirements") - } - } else { + ch, err := loader.Load(chartPath) + if err != nil { return err } + if req := ch.Metadata.Requirements; req != nil { + if err := checkDependencies(ch, req); err != nil { + return err + } + } resp, err := o.client.UpdateRelease( o.release, diff --git a/cmd/helm/upgrade_test.go b/cmd/helm/upgrade_test.go index c75b50b1a..15b4067c8 100644 --- a/cmd/helm/upgrade_test.go +++ b/cmd/helm/upgrade_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -19,8 +19,9 @@ package main import ( "testing" + "k8s.io/helm/pkg/chart" + "k8s.io/helm/pkg/chart/loader" "k8s.io/helm/pkg/chartutil" - "k8s.io/helm/pkg/hapi/chart" "k8s.io/helm/pkg/hapi/release" "k8s.io/helm/pkg/helm" ) @@ -36,7 +37,7 @@ func TestUpgradeCmd(t *testing.T) { if err != nil { t.Fatalf("Error creating chart for upgrade: %v", err) } - ch, err := chartutil.Load(chartPath) + ch, err := loader.Load(chartPath) if err != nil { t.Fatalf("Error loading chart: %v", err) } @@ -56,7 +57,7 @@ func TestUpgradeCmd(t *testing.T) { if err != nil { t.Fatalf("Error creating chart: %v", err) } - ch, err = chartutil.Load(chartPath) + ch, err = loader.Load(chartPath) if err != nil { t.Fatalf("Error loading updated chart: %v", err) } @@ -73,7 +74,7 @@ func TestUpgradeCmd(t *testing.T) { t.Fatalf("Error creating chart: %v", err) } var ch2 *chart.Chart - ch2, err = chartutil.Load(chartPath) + ch2, err = loader.Load(chartPath) if err != nil { t.Fatalf("Error loading updated chart: %v", err) } diff --git a/cmd/helm/verify.go b/cmd/helm/verify.go index 5d0ee221a..f46e1570f 100644 --- a/cmd/helm/verify.go +++ b/cmd/helm/verify.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/cmd/helm/verify_test.go b/cmd/helm/verify_test.go index ccc2ff475..f4ddc42f1 100644 --- a/cmd/helm/verify_test.go +++ b/cmd/helm/verify_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/cmd/helm/version.go b/cmd/helm/version.go index 857539fe1..4b895bf06 100644 --- a/cmd/helm/version.go +++ b/cmd/helm/version.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/cmd/helm/version_test.go b/cmd/helm/version_test.go index 0573dbf06..7be08e72d 100644 --- a/cmd/helm/version_test.go +++ b/cmd/helm/version_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/docs/architecture.md b/docs/architecture.md index 752a7e12c..6b02db1f4 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -24,41 +24,33 @@ For Helm, there are three important concepts: ## Components -Helm has two major components: +Helm is an executable which is implemented into two distinct parts: **The Helm Client** is a command-line client for end users. The client -is responsible for the following domains: +is responsible for the following: - Local chart development - Managing repositories -- Interacting with the Tiller server +- Managing releases +- Interfacing with the Helm library - Sending charts to be installed - - Asking for information about releases - Requesting upgrading or uninstalling of existing releases -**The Tiller Server** is an in-cluster server that interacts with the -Helm client, and interfaces with the Kubernetes API server. The server -is responsible for the following: +**The Helm Library** provides the logic for executing all Helm operations. +It interfaces with the Kubernetes API server and provides the following capability: -- Listening for incoming requests from the Helm client - Combining a chart and configuration to build a release -- Installing charts into Kubernetes, and then tracking the subsequent - release +- Installing charts into Kubernetes, and providing the subsequent release object - Upgrading and uninstalling charts by interacting with Kubernetes -In a nutshell, the client is responsible for managing charts, and the -server is responsible for managing releases. +The standalone Helm library encapsulates the Helm logic so that it can be leveraged by different clients. ## Implementation -The Helm client is written in the Go programming language, and uses the -gRPC protocol suite to interact with the Tiller server. - -The Tiller server is also written in Go. It provides a gRPC server to -connect with the client, and it uses the Kubernetes client library to -communicate with Kubernetes. Currently, that library uses REST+JSON. +The Helm client and library is written in the Go programming language. -The Tiller server stores information in ConfigMaps located inside of -Kubernetes. It does not need its own database. +The library uses the Kubernetes client library to communicate with Kubernetes. Currently, +that library uses REST+JSON. It stores information in Secrets located inside of Kubernetes. +It does not need its own database. Configuration files are, when possible, written in YAML. diff --git a/docs/chart_best_practices/conventions.md b/docs/chart_best_practices/conventions.md index 90a25551f..55624a558 100644 --- a/docs/chart_best_practices/conventions.md +++ b/docs/chart_best_practices/conventions.md @@ -28,19 +28,17 @@ When SemVer versions are stored in Kubernetes labels, we conventionally alter th YAML files should be indented using _two spaces_ (and never tabs). -## Usage of the Words Helm, Tiller, and Chart +## Usage of the Words Helm and Chart -There are a few small conventions followed for using the words Helm, helm, Tiller, and tiller. +There are a few small conventions followed for using the words Helm and helm. - Helm refers to the project, and is often used as an umbrella term - `helm` refers to the client-side command -- Tiller is the proper name of the backend -- `tiller` is the name of the binary run on the backend - The term 'chart' does not need to be capitalized, as it is not a proper noun. When in doubt, use _Helm_ (with an uppercase 'H'). -## Restricting Tiller by Version +## Restricting Helm by Version A `Chart.yaml` file can specify a `helmVersion` SemVer constraint: @@ -55,5 +53,5 @@ supported in older versions of Helm. While this parameter will accept sophistica SemVer rules, the best practice is to default to the form `>=2.4.0`, where `2.4.0` is the version that introduced the new feature used in the chart. -This feature was introduced in Helm 2.4.0, so any version of Tiller older than +This feature was introduced in Helm 2.4.0, so any version of Helm older than 2.4.0 will simply ignore this field. diff --git a/docs/chart_best_practices/custom_resource_definitions.md b/docs/chart_best_practices/custom_resource_definitions.md index 96690dc9b..fc390e258 100644 --- a/docs/chart_best_practices/custom_resource_definitions.md +++ b/docs/chart_best_practices/custom_resource_definitions.md @@ -34,4 +34,4 @@ To package the two together, add a `pre-install` hook to the CRD definition so that it is fully installed before the rest of the chart is executed. Note that if you create the CRD with a `pre-install` hook, that CRD definition -will not be deleted when `helm delete` is run. +will not be uninstalled when `helm uninstall` is run. diff --git a/docs/chart_best_practices/labels.md b/docs/chart_best_practices/labels.md index 7c3ac51db..1dbe27476 100644 --- a/docs/chart_best_practices/labels.md +++ b/docs/chart_best_practices/labels.md @@ -25,7 +25,6 @@ are recommended, and _should_ be placed onto a chart for global consistency. Tho Name|Status|Description -----|------|---------- -heritage | REC | This should always be set to `{{ .Release.Service }}`. It is for finding all things managed by Tiller. release | REC | This should be the `{{ .Release.Name }}`. chart | REC | This should be the chart name and version: `{{ .Chart.Name }}-{{ .Chart.Version \| replace "+" "_" }}`. app | REC | This should be the app name, reflecting the entire app. Usually `{{ template "name" . }}` is used for this. This is used by many Kubernetes manifests, and is not Helm-specific. diff --git a/docs/chart_repository_faq.md b/docs/chart_repository_faq.md index a3e6392ba..2b01612e9 100644 --- a/docs/chart_repository_faq.md +++ b/docs/chart_repository_faq.md @@ -6,9 +6,9 @@ This section tracks some of the more frequently encountered issues with using ch information, [file an issue](https://github.com/kubernetes/helm/issues) or send us a pull request. -## Fetching +## Pulling -**Q: Why do I get a `unsupported protocol scheme ""` error when trying to fetch a chart from my custom repo?** +**Q: Why do I get a `unsupported protocol scheme ""` error when trying to pull a chart from my custom repo?** A: (Helm < 2.5.0) This is likely caused by you creating your chart repo index without specifying the `--url` flag. Try recreating your `index.yaml` file with a command like `helm repo index --url http://my-repo/charts .`, diff --git a/docs/chart_template_guide/accessing_files.md b/docs/chart_template_guide/accessing_files.md index 250fd9520..3d46f3d1e 100644 --- a/docs/chart_template_guide/accessing_files.md +++ b/docs/chart_template_guide/accessing_files.md @@ -4,7 +4,7 @@ In the previous section we looked at several ways to create and access named tem Helm provides access to files through the `.Files` object. Before we get going with the template examples, though, there are a few things to note about how this works: -- It is okay to add extra files to your Helm chart. These files will be bundled and sent to Tiller. Be careful, though. Charts must be smaller than 1M because of the storage limitations of Kubernetes objects. +- It is okay to add extra files to your Helm chart. These files will be bundled. Be careful, though. Charts must be smaller than 1M because of the storage limitations of Kubernetes objects. - Some files cannot be accessed through the `.Files` object, usually for security reasons. - Files in `templates/` cannot be accessed. - Files excluded using `.helmignore` cannot be accessed. diff --git a/docs/chart_template_guide/builtin_objects.md b/docs/chart_template_guide/builtin_objects.md index de2145d05..267b3cac4 100644 --- a/docs/chart_template_guide/builtin_objects.md +++ b/docs/chart_template_guide/builtin_objects.md @@ -8,7 +8,6 @@ In the previous section, we use `{{.Release.Name}}` to insert the name of a rele - `Release`: This object describes the release itself. It has several objects inside of it: - `Release.Name`: The release name - - `Release.Service`: The name of the releasing service (always `Tiller`). - `Release.IsUpgrade`: This is set to `true` if the current operation is an upgrade or rollback. - `Release.IsInstall`: This is set to `true` if the current operation is an install. - `Values`: Values passed into the template from the `values.yaml` file and from user-supplied files. By default, `Values` is empty. @@ -21,7 +20,7 @@ In the previous section, we use `{{.Release.Name}}` to insert the name of a rele - `Capabilities.APIVersions` is a set of versions. - `Capabilities.APIVersions.Has $version` indicates whether a version (`batch/v1`) is enabled on the cluster. - `Capabilities.KubeVersion` provides a way to look up the Kubernetes version. It has the following values: `Major`, `Minor`, `GitVersion`, `GitCommit`, `GitTreeState`, `BuildDate`, `GoVersion`, `Compiler`, and `Platform`. - - `Capabilities.helmVersion` provides a way to look up the Tiller version. It has the following values: `SemVer`, `GitCommit`, and `GitTreeState`. + - `Capabilities.HelmVersion` provides a way to look up the Helm version. It has the following values: `SemVer`, `GitCommit`, and `GitTreeState`. - `Template`: Contains information about the current template that is being executed - `Name`: A namespaced filepath to the current template (e.g. `mychart/templates/mytemplate.yaml`) - `BasePath`: The namespaced path to the templates directory of the current chart (e.g. `mychart/templates`). diff --git a/docs/chart_template_guide/debugging.md b/docs/chart_template_guide/debugging.md index fac788cc4..050a2e3ca 100644 --- a/docs/chart_template_guide/debugging.md +++ b/docs/chart_template_guide/debugging.md @@ -1,6 +1,6 @@ # Debugging Templates -Debugging templates can be tricky simply because the templates are rendered on the Tiller server, not the Helm client. And then the rendered templates are sent to the Kubernetes API server, which may reject the YAML files for reasons other than formatting. +Debugging templates can be tricky because the rendered templates are sent to the Kubernetes API server, which may reject the YAML files for reasons other than formatting. There are a few commands that can help you debug. diff --git a/docs/chart_template_guide/getting_started.md b/docs/chart_template_guide/getting_started.md index 87ae5fa3c..4971ef05f 100644 --- a/docs/chart_template_guide/getting_started.md +++ b/docs/chart_template_guide/getting_started.md @@ -18,9 +18,9 @@ mychart/ ... ``` -The `templates/` directory is for template files. When Tiller evaluates a chart, +The `templates/` directory is for template files. When Helm evaluates a chart, it will send all of the files in the `templates/` directory through the -template rendering engine. Tiller then collects the results of those templates +template rendering engine. It then collects the results of those templates and sends them on to Kubernetes. The `values.yaml` file is also important to templates. This file contains the @@ -90,7 +90,7 @@ In virtue of the fact that this file is in the `templates/` directory, it will be sent through the template engine. It is just fine to put a plain YAML file like this in the `templates/` directory. -When Tiller reads this template, it will simply send it to Kubernetes as-is. +When Helm reads this template, it will simply send it to Kubernetes as-is. With this simple template, we now have an installable chart. And we can install it like this: @@ -133,7 +133,7 @@ generated this YAML document. From there on, we can see that the YAML data is exactly what we put in our `configmap.yaml` file. -Now we can delete our release: `helm delete full-coral`. +Now we can uninstall our release: `helm uninstall full-coral`. ### Adding a Simple Template Call @@ -165,7 +165,7 @@ The template directive `{{ .Release.Name }}` injects the release name into the t The leading dot before `Release` indicates that we start with the top-most namespace for this scope (we'll talk about scope in a bit). So we could read `.Release.Name` as "start at the top namespace, find the `Release` object, then look inside of it for an object called `Name`". -The `Release` object is one of the built-in objects for Helm, and we'll cover it in more depth later. But for now, it is sufficient to say that this will display the release name that Tiller assigns to our release. +The `Release` object is one of the built-in objects for Helm, and we'll cover it in more depth later. But for now, it is sufficient to say that this will display the release name that the library assigns to our release. Now when we install our resource, we'll immediately see the result of using this template directive: @@ -187,7 +187,7 @@ instead of `mychart-configmap`. You can run `helm get manifest clunky-serval` to see the entire generated YAML. -At this point, we've seen templates at their most basic: YAML files that have template directives embedded in `{{` and `}}`. In the next part, we'll take a deeper look into templates. But before moving on, there's one quick trick that can make building templates faster: When you want to test the template rendering, but not actually install anything, you can use `helm install --debug --dry-run ./mychart`. This will send the chart to the Tiller server, which will render the templates. But instead of installing the chart, it will return the rendered template to you so you can see the output: +At this point, we've seen templates at their most basic: YAML files that have template directives embedded in `{{` and `}}`. In the next part, we'll take a deeper look into templates. But before moving on, there's one quick trick that can make building templates faster: When you want to test the template rendering, but not actually install anything, you can use `helm install --debug --dry-run ./mychart`. This will render the templates. But instead of installing the chart, it will return the rendered template to you so you can see the output: ```console $ helm install --debug --dry-run ./mychart diff --git a/docs/charts.md b/docs/charts.md index f41377048..b572b65a1 100644 --- a/docs/charts.md +++ b/docs/charts.md @@ -58,7 +58,6 @@ engine: gotpl # The name of the template engine (optional, defaults to gotpl) icon: A URL to an SVG or PNG image to be used as an icon (optional). appVersion: The version of the app that this contains (optional). This needn't be SemVer. deprecated: Whether this chart is deprecated (optional, boolean) -helmVersion: The version of Tiller that this chart requires. This should be expressed as a SemVer range: ">2.0.0" (optional) ``` If you are familiar with the `Chart.yaml` file format for Helm Classic, you will @@ -91,7 +90,7 @@ rely upon or require GitHub or even Git. Consequently, it does not use Git SHAs for versioning at all. The `version` field inside of the `Chart.yaml` is used by many of the -Helm tools, including the CLI and the Tiller server. When generating a +Helm tools, including the CLI. When generating a package, the `helm package` command will use the version that it finds in the `Chart.yaml` as a token in the package name. The system assumes that the version number in the chart package name matches the version number in @@ -449,7 +448,7 @@ on Apache and MySQL by including those charts inside of its `charts/` directory. **TIP:** _To drop a dependency into your `charts/` directory, use the -`helm fetch` command_ +`helm pull` command_ ### Operational aspects of using dependencies @@ -488,7 +487,7 @@ the Kubernetes objects from the charts and all its dependencies are Hence a single release is created with all the objects for the chart and its dependencies. The install order of Kubernetes types is given by the enumeration InstallOrder in kind_sorter.go -(see [the Helm source file](https://github.com/kubernetes/helm/blob/master/pkg/tiller/kind_sorter.go#L26)). +(see [the Helm source file](https://github.com/helm/helm/blob/dev-v3/pkg/tiller/kind_sorter.go#L26)). ## Templates and Values @@ -574,8 +573,7 @@ cannot be overridden. As with all values, the names are _case sensitive_. - `Release.Name`: The name of the release (not the chart) -- `Release.Service`: The service that conducted the release. Usually - this is `Tiller`. +- `Release.Service`: The service that conducted the release. - `Release.IsUpgrade`: This is set to true if the current operation is an upgrade or rollback. - `Release.IsInstall`: This is set to true if the current operation is an install. @@ -589,9 +587,9 @@ sensitive_. `{{.Files.GetString name}}` functions. You can also access the contents of the file as `[]byte` using `{{.Files.GetBytes}}` - `Capabilities`: A map-like object that contains information about the versions - of Kubernetes (`{{.Capabilities.KubeVersion}}`, Tiller - (`{{.Capabilities.HelmVersion}}`, and the supported Kubernetes API versions - (`{{.Capabilities.APIVersions.Has "batch/v1"`) + of Kubernetes (`{{.Capabilities.KubeVersion}}`, Helm + (`{{.Capabilities.HelmVersion}}`, and the supported Kubernetes + API versions (`{{.Capabilities.APIVersions.Has "batch/v1"`) **NOTE:** Any unknown Chart.yaml fields will be dropped. They will not be accessible inside of the `Chart` object. Thus, Chart.yaml cannot be diff --git a/docs/charts_hooks.md b/docs/charts_hooks.md index 4142f2ce0..347972209 100644 --- a/docs/charts_hooks.md +++ b/docs/charts_hooks.md @@ -45,10 +45,10 @@ consider the lifecycle for a `helm install`. By default, the lifecycle looks like this: 1. User runs `helm install foo` -2. Chart is loaded into Tiller -3. After some verification, Tiller renders the `foo` templates -4. Tiller loads the resulting resources into Kubernetes -5. Tiller returns the release name (and other data) to the client +2. The Helm library install API is called +3. After some verification, the library renders the `foo` templates +4. The library loads the resulting resources into Kubernetes +5. The library returns the release object (and other data) to the client 6. The client exits Helm defines two hooks for the `install` lifecycle: `pre-install` and @@ -56,24 +56,24 @@ Helm defines two hooks for the `install` lifecycle: `pre-install` and hooks, the lifecycle is altered like this: 1. User runs `helm install foo` -2. Chart is loaded into Tiller -3. After some verification, Tiller renders the `foo` templates -4. Tiller prepares to execute the `pre-install` hooks (loading hook resources into +2. The Helm library install API is called +3. After some verification, the library renders the `foo` templates +4. The library prepares to execute the `pre-install` hooks (loading hook resources into Kubernetes) -5. Tiller sorts hooks by weight (assigning a weight of 0 by default) and by name for those hooks with the same weight in ascending order. -6. Tiller then loads the hook with the lowest weight first (negative to positive) -7. Tiller waits until the hook is "Ready" -8. Tiller loads the resulting resources into Kubernetes. Note that if the `--wait` -flag is set, Tiller will wait until all resources are in a ready state +5. The library sorts hooks by weight (assigning a weight of 0 by default) and by name for those hooks with the same weight in ascending order. +6. The library then loads the hook with the lowest weight first (negative to positive) +7. The library waits until the hook is "Ready" (except for CRDs) +8. The library loads the resulting resources into Kubernetes. Note that if the `--wait` +flag is set, the library will wait until all resources are in a ready state and will not run the `post-install` hook until they are ready. -9. Tiller executes the `post-install` hook (loading hook resources) -10. Tiller waits until the hook is "Ready" -11. Tiller returns the release name (and other data) to the client +9. The library executes the `post-install` hook (loading hook resources) +10. The library waits until the hook is "Ready" +11. The library returns the release object (and other data) to the client 12. The client exits What does it mean to wait until a hook is ready? This depends on the -resource declared in the hook. If the resources is a `Job` kind, Tiller -will wait until the job successfully runs to completion. And if the job +resource declared in the hook. If the resources is a `Job` kind, the library + will wait until the job successfully runs to completion. And if the job fails, the release will fail. This is a _blocking operation_, so the Helm client will pause while the Job is run. @@ -90,11 +90,11 @@ to `0` if weight is not important. ### Hook resources are not managed with corresponding releases The resources that a hook creates are not tracked or managed as part of the -release. Once Tiller verifies that the hook has reached its ready state, it +release. Once Helm verifies that the hook has reached its ready state, it will leave the hook resource alone. Practically speaking, this means that if you create resources in a hook, you -cannot rely upon `helm delete` to remove the resources. To destroy such +cannot rely upon `helm uninstall` to remove the resources. To destroy such resources, you need to either write code to perform this operation in a `pre-delete` or `post-delete` hook or add `"helm.sh/hook-delete-policy"` annotation to the hook template file. @@ -170,7 +170,7 @@ deterministic executing order. Weights are defined using the following annotatio ``` Hook weights can be positive or negative numbers but must be represented as -strings. When Tiller starts the execution cycle of hooks of a particular Kind it +strings. When Helm starts the execution cycle of hooks of a particular Kind it will sort those hooks in ascending order. It is also possible to define policies that determine when to delete corresponding hook resources. Hook deletion policies are defined using the following annotation: @@ -181,11 +181,11 @@ It is also possible to define policies that determine when to delete correspondi ``` You can choose one or more defined annotation values: -* `"hook-succeeded"` specifies Tiller should delete the hook after the hook is successfully executed. -* `"hook-failed"` specifies Tiller should delete the hook if the hook failed during execution. -* `"before-hook-creation"` specifies Tiller should delete the previous hook before the new hook is launched. +* `"hook-succeeded"` specifies Helm should delete the hook after the hook is successfully executed. +* `"hook-failed"` specifies Helm should delete the hook if the hook failed during execution. +* `"before-hook-creation"` specifies Helm should delete the previous hook before the new hook is launched. -### Automatically delete hook from previous release +### Automatically uninstall hook from previous release When helm release being updated it is possible, that hook resource already exists in cluster. By default helm will try to create resource and fail with `"... already exists"` error. @@ -195,4 +195,4 @@ One might choose `"helm.sh/hook-delete-policy": "before-hook-creation"` over `"h * It may be necessary to keep succeeded hook resource in kubernetes for some reason. * At the same time it is not desirable to do manual resource deletion before helm release upgrade. -`"helm.sh/hook-delete-policy": "before-hook-creation"` annotation on hook causes tiller to remove the hook from previous release if there is one before the new hook is launched and can be used with another policy. +`"helm.sh/hook-delete-policy": "before-hook-creation"` annotation on hook causes Helm to remove the hook from previous release if there is one before the new hook is launched and can be used with another policy. diff --git a/docs/charts_tips_and_tricks.md b/docs/charts_tips_and_tricks.md index 484d8b936..705cd42fe 100644 --- a/docs/charts_tips_and_tricks.md +++ b/docs/charts_tips_and_tricks.md @@ -9,10 +9,8 @@ Helm uses [Go templates](https://godoc.org/text/template) for templating your resource files. While Go ships several built-in functions, we have added many others. -First, we added almost all of the functions in the -[Sprig library](https://godoc.org/github.com/Masterminds/sprig). We removed two -for security reasons: `env` and `expandenv` (which would have given chart authors -access to Tiller's environment). +First, we added all of the functions in the +[Sprig library](https://godoc.org/github.com/Masterminds/sprig). We also added two special template functions: `include` and `required`. The `include` function allows you to bring in another template, and then pass the results to other @@ -160,11 +158,11 @@ spec: See also the `helm upgrade --recreate-pods` flag for a slightly different way of addressing this issue. -## Tell Tiller Not To Delete a Resource +## Tell Helm Not To Uninstall a Resource -Sometimes there are resources that should not be deleted when Helm runs a -`helm delete`. Chart developers can add an annotation to a resource to prevent -it from being deleted. +Sometimes there are resources that should not be uninstalled when Helm runs a +`helm uninstall`. Chart developers can add an annotation to a resource to prevent +it from being uninstalled. ```yaml kind: Secret @@ -176,10 +174,10 @@ metadata: (Quotation marks are required) -The annotation `"helm.sh/resource-policy": keep` instructs Tiller to skip this -resource during a `helm delete` operation. _However_, this resource becomes +The annotation `"helm.sh/resource-policy": keep` instructs Helm to skip this +resource during a `helm uninstall` operation. _However_, this resource becomes orphaned. Helm will no longer manage it in any way. This can lead to problems -if using `helm install --replace` on a release that has already been deleted, but +if using `helm install --replace` on a release that has already been uninstalled, but has kept resources. ## Using "Partials" and Template Includes diff --git a/docs/developers.md b/docs/developers.md index 983e47d84..8430babeb 100644 --- a/docs/developers.md +++ b/docs/developers.md @@ -1,17 +1,16 @@ # Developers Guide This guide explains how to set up your environment for developing on -Helm and Tiller. +Helm. ## Prerequisites - The latest version of Go - The latest version of Dep - A Kubernetes cluster w/ kubectl (optional) -- The gRPC toolchain - Git -## Building Helm/Tiller +## Building Helm We use Make to build our programs. The simplest way to get started is: @@ -23,18 +22,15 @@ NOTE: This will fail if not running from the path `$GOPATH/src/k8s.io/helm`. The directory `k8s.io` should not be a symlink or `build` will not find the relevant packages. -This will build both Helm and Tiller. `make bootstrap` will attempt to +This will build both Helm and the Helm library. `make bootstrap` will attempt to install certain tools if they are missing. To run all the tests (without running the tests for `vendor/`), run `make test`. -To run Helm and Tiller locally, you can run `bin/helm` or `bin/tiller`. +To run Helm locally, you can run `bin/helm`. -- Helm and Tiller are known to run on macOS and most Linuxes, including - Alpine. -- Tiller must have access to a Kubernetes cluster. It learns about the - cluster by examining the Kube config files that `kubectl` uses. +- Helm is known to run on macOS and most Linuxes, including Alpine. ### Man pages @@ -49,30 +45,6 @@ $ export MANPATH=$GOPATH/src/k8s.io/helm/docs/man:$MANPATH $ man helm ``` -## gRPC and Protobuf - -Helm and Tiller communicate using gRPC. To get started with gRPC, you will need to... - -- Install `protoc` for compiling protobuf files. Releases are - [here](https://github.com/google/protobuf/releases) -- Run Helm's `make bootstrap` to generate the `protoc-gen-go` plugin and - place it in `bin/`. - -Note that you need to be on protobuf 3.2.0 (`protoc --version`). The -version of `protoc-gen-go` is tied to the version of gRPC used in -Kubernetes. So the plugin is maintained locally. - -While the gRPC and ProtoBuf specs remain silent on indentation, we -require that the indentation style matches the Go format specification. -Namely, protocol buffers should use tab-based indentation and rpc -declarations should follow the style of Go function declarations. - -### The Helm API (HAPI) - -We use gRPC as an API layer. See `pkg/proto/hapi` for the generated Go code, -and `_proto` for the protocol buffer definitions. - -To regenerate the Go files from the protobuf source, `make protoc`. ## Docker Images @@ -85,41 +57,7 @@ GCR registry. For development, we highly recommend using the [Kubernetes Minikube](https://github.com/kubernetes/minikube) -developer-oriented distribution. Once this is installed, you can use -`helm init` to install into the cluster. Note that version of tiller you're using for -development may not be available in Google Cloud Container Registry. If you're getting -image pull errors, you can override the version of Tiller. Example: - -```console -helm init --tiller-image=gcr.io/kubernetes-helm/tiller:2.7.2 -``` - -Or use the latest version: - -```console -helm init --canary-image -``` - -For developing on Tiller, it is sometimes more expedient to run Tiller locally -instead of packaging it into an image and running it in-cluster. You can do -this by telling the Helm client to us a local instance. - -```console -$ make build -$ bin/tiller -``` - -And to configure the Helm client, use the `--host` flag or export the `HELM_HOST` -environment variable: - -```console -$ export HELM_HOST=localhost:44134 -$ helm install foo -``` - -(Note that you do not need to use `helm init` when you are running Tiller directly) - -Tiller should run on any >= 1.3 Kubernetes cluster. +developer-oriented distribution. ## Contribution Guidelines @@ -191,8 +129,6 @@ Common commit types: Common scopes: - helm: The Helm CLI -- tiller: The Tiller server -- proto: Protobuf definitions - pkg/lint: The lint package. Follow a similar convention for any package - `*`: two or more scopes diff --git a/docs/examples/nginx/charts/alpine/Chart.yaml b/docs/examples/nginx/charts/alpine/Chart.yaml new file mode 100644 index 000000000..f4b660d4f --- /dev/null +++ b/docs/examples/nginx/charts/alpine/Chart.yaml @@ -0,0 +1,7 @@ +name: alpine +description: Deploy a basic Alpine Linux pod +version: 0.1.0 +home: https://github.com/kubernetes/helm +sources: + - https://github.com/kubernetes/helm +appVersion: 3.3 diff --git a/docs/examples/nginx/charts/alpine/README.md b/docs/examples/nginx/charts/alpine/README.md new file mode 100644 index 000000000..3e354724c --- /dev/null +++ b/docs/examples/nginx/charts/alpine/README.md @@ -0,0 +1,11 @@ +# Alpine: A simple Helm chart + +Run a single pod of Alpine Linux. + +The `templates/` directory contains a very simple pod resource with a +couple of parameters. + +The `values.yaml` file contains the default values for the +`alpine-pod.yaml` template. + +You can install this example using `helm install docs/examples/alpine`. diff --git a/docs/examples/nginx/charts/alpine/templates/_helpers.tpl b/docs/examples/nginx/charts/alpine/templates/_helpers.tpl new file mode 100644 index 000000000..3e9c25bed --- /dev/null +++ b/docs/examples/nginx/charts/alpine/templates/_helpers.tpl @@ -0,0 +1,16 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "alpine.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "alpine.fullname" -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/docs/examples/nginx/charts/alpine/templates/alpine-pod.yaml b/docs/examples/nginx/charts/alpine/templates/alpine-pod.yaml new file mode 100644 index 000000000..da9caef78 --- /dev/null +++ b/docs/examples/nginx/charts/alpine/templates/alpine-pod.yaml @@ -0,0 +1,23 @@ +apiVersion: v1 +kind: Pod +metadata: + name: {{ template "alpine.fullname" . }} + labels: + # The "heritage" label is used to track which tool deployed a given chart. + # It is useful for admins who want to see what releases a particular tool + # is responsible for. + heritage: {{ .Release.Service }} + # The "release" convention makes it easy to tie a release to all of the + # Kubernetes resources that were created as part of that release. + release: {{ .Release.Name }} + # This makes it easy to audit chart usage. + chart: {{ .Chart.Name }}-{{ .Chart.Version }} + app: {{ template "alpine.name" . }} +spec: + # This shows how to use a simple value. This will look for a passed-in value called restartPolicy. + restartPolicy: {{ .Values.restartPolicy }} + containers: + - name: waiter + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + command: ["/bin/sleep", "9000"] diff --git a/docs/examples/nginx/charts/alpine/values.yaml b/docs/examples/nginx/charts/alpine/values.yaml new file mode 100644 index 000000000..afe8cc6c0 --- /dev/null +++ b/docs/examples/nginx/charts/alpine/values.yaml @@ -0,0 +1,6 @@ +image: + repository: alpine + tag: 3.3 + pullPolicy: IfNotPresent + +restartPolicy: Never diff --git a/docs/examples/nginx/templates/NOTES.txt b/docs/examples/nginx/templates/NOTES.txt new file mode 100644 index 000000000..4bdf443f6 --- /dev/null +++ b/docs/examples/nginx/templates/NOTES.txt @@ -0,0 +1 @@ +Sample notes for {{ .Chart.Name }} \ No newline at end of file diff --git a/docs/glossary.md b/docs/glossary.md index 875807268..b40ad5aa0 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -94,7 +94,7 @@ chart repository server or any other HTTP server. ## Release -When a chart is installed, Tiller (the Helm server) creates a _release_ +When a chart is installed, the Helm library creates a _release_ to track that installation. A single chart may be installed many times into the same cluster, and @@ -130,12 +130,10 @@ rollback 1| release 4 (but running the same config as release 1) The above table illustrates how release numbers increment across install, upgrade, and rollback. -## Tiller +## Helm Library -Tiller is the in-cluster component of Helm. It interacts directly with -the Kubernetes API server to install, upgrade, query, and remove -Kubernetes resources. It also stores the objects that represent -releases. +It interacts directly with the Kubernetes API server to install, + upgrade, query, and remove Kubernetes resources. ## Repository (Repo, Chart Repository) diff --git a/docs/history.md b/docs/history.md index 71e63c6b2..a1cda57c1 100644 --- a/docs/history.md +++ b/docs/history.md @@ -7,8 +7,7 @@ is now part of the CNCF. Many companies now contribute regularly to Helm. Differences from Helm Classic: -- Helm now has both a client (`helm`) and a server (`tiller`). The - server runs inside of Kubernetes, and manages your resources. +- Helm now has both a client (`helm`) and a library. In version 2 it had a server (`tiller`) but the capability is now contained within the library. - Helm's chart format has changed for the better: - Dependencies are immutable and stored inside of a chart's `charts/` directory. diff --git a/docs/index.md b/docs/index.md index 4ca93bd1f..b39140637 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,13 +1,12 @@ # Helm Documentation - [Quick Start](quickstart.md) - Read me first! -- [Installing Helm](install.md) - Install Helm and Tiller +- [Installing Helm](install.md) - Install Helm - [Kubernetes Distribution Notes](kubernetes_distros.md) - [Frequently Asked Questions](install_faq.md) - [Using Helm](using_helm.md) - Learn the Helm tools - [Plugins](plugins.md) - [Role-based Access Control](rbac.md) - - [TLS/SSL for Helm and Tiller](tiller_ssl.md) - Use Helm-to-Tiller encryption - [Developing Charts](charts.md) - An introduction to chart development - [Chart Lifecycle Hooks](charts_hooks.md) - [Chart Tips and Tricks](charts_tips_and_tricks.md) @@ -31,7 +30,7 @@ - [Appendix A: YAML Techniques](chart_template_guide/yaml_techniques.md) - [Appendix B: Go Data Types](chart_template_guide/data_types.md) - [Related Projects](related.md) - More Helm tools, articles, and plugins -- [Architecture](architecture.md) - Overview of the Helm/Tiller design +- [Architecture](architecture.md) - Overview of the Helm design - [Developers](developers.md) - About the developers - [History](history.md) - A brief history of the project - [Glossary](glossary.md) - Decode the Helm vocabulary diff --git a/docs/install.md b/docs/install.md index 25f77ba91..6e07c00b8 100755 --- a/docs/install.md +++ b/docs/install.md @@ -1,15 +1,12 @@ # Installing Helm There are two parts to Helm: The Helm client (`helm`) and the Helm -server (Tiller). This guide shows how to install the client, and then -proceeds to show two ways to install the server. +library. This guide shows how to install both together. -**IMPORTANT**: If you are responsible for ensuring your cluster is a controlled environment, especially when resources are shared, it is strongly recommended installing Tiller using a secured configuration. For guidance, see [Securing your Helm Installation](securing_installation.md). -## Installing the Helm Client +## Installing Helm -The Helm client can be installed either from source, or from pre-built binary -releases. +Helm can be installed either from source, or from pre-built binary releases. ### From the Binary Releases @@ -48,7 +45,7 @@ choco install kubernetes-helm ## From Script Helm now has an installer script that will automatically grab the latest version -of the Helm client and [install it locally](https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get). +of Helm and [install it locally](https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get). You can fetch that script, and then execute it locally. It's well documented so that you can read through it and understand what it is doing before you run it. @@ -96,255 +93,7 @@ The `bootstrap` target will attempt to install dependencies, rebuild the `vendor/` tree, and validate configuration. The `build` target will compile `helm` and place it in `bin/helm`. -Tiller is also compiled, and is placed in `bin/tiller`. -## Installing Tiller - -Tiller, the server portion of Helm, typically runs inside of your -Kubernetes cluster. But for development, it can also be run locally, and -configured to talk to a remote Kubernetes cluster. - -### Easy In-Cluster Installation - -The easiest way to install `tiller` into the cluster is simply to run -`helm init`. This will validate that `helm`'s local environment is set -up correctly (and set it up if necessary). Then it will connect to -whatever cluster `kubectl` connects to by default (`kubectl config -view`). Once it connects, it will install `tiller` into the -`kube-system` namespace. - -After `helm init`, you should be able to run `kubectl get pods --namespace -kube-system` and see Tiller running. - -You can explicitly tell `helm init` to... - -- Install the canary build with the `--canary-image` flag -- Install a particular image (version) with `--tiller-image` -- Install to a particular cluster with `--kube-context` -- Install into a particular namespace with `--tiller-namespace` - -Once Tiller is installed, running `helm version` should show you both -the client and server version. (If it shows only the client version, -`helm` cannot yet connect to the server. Use `kubectl` to see if any -`tiller` pods are running.) - -Helm will look for Tiller in the `kube-system` namespace unless -`--tiller-namespace` or `TILLER_NAMESPACE` is set. - -### Installing Tiller Canary Builds - -Canary images are built from the `master` branch. They may not be -stable, but they offer you the chance to test out the latest features. - -The easiest way to install a canary image is to use `helm init` with the -`--canary-image` flag: - -```console -$ helm init --canary-image -``` - -This will use the most recently built container image. You can always -uninstall Tiller by deleting the Tiller deployment from the -`kube-system` namespace using `kubectl`. - -### Running Tiller Locally - -For development, it is sometimes easier to work on Tiller locally, and -configure it to connect to a remote Kubernetes cluster. - -The process of building Tiller is explained above. - -Once `tiller` has been built, simply start it: - -```console -$ bin/tiller -Tiller running on :44134 -``` - -When Tiller is running locally, it will attempt to connect to the -Kubernetes cluster that is configured by `kubectl`. (Run `kubectl config -view` to see which cluster that is.) - -You must tell `helm` to connect to this new local Tiller host instead of -connecting to the one in-cluster. There are two ways to do this. The -first is to specify the `--host` option on the command line. The second -is to set the `$HELM_HOST` environment variable. - -```console -$ export HELM_HOST=localhost:44134 -$ helm version # Should connect to localhost. -Client: &version.Version{SemVer:"v2.0.0-alpha.4", GitCommit:"db...", GitTreeState:"dirty"} -Server: &version.Version{SemVer:"v2.0.0-alpha.4", GitCommit:"a5...", GitTreeState:"dirty"} -``` - -Importantly, even when running locally, Tiller will store release -configuration in ConfigMaps inside of Kubernetes. - -## Upgrading Tiller - -As of Helm 2.2.0, Tiller can be upgraded using `helm init --upgrade`. - -For older versions of Helm, or for manual upgrades, you can use `kubectl` to modify -the Tiller image: - -```console -$ export TILLER_TAG=v2.0.0-beta.1 # Or whatever version you want -$ kubectl --namespace=kube-system set image deployments/tiller-deploy tiller=gcr.io/kubernetes-helm/tiller:$TILLER_TAG -deployment "tiller-deploy" image updated -``` - -Setting `TILLER_TAG=canary` will get the latest snapshot of master. - -## Deleting or Reinstalling Tiller - -Because Tiller stores its data in Kubernetes ConfigMaps, you can safely -delete and re-install Tiller without worrying about losing any data. The -recommended way of deleting Tiller is with `kubectl delete deployment -tiller-deploy --namespace kube-system`, or more concisely `helm reset`. - -Tiller can then be re-installed from the client with: - -```console -$ helm init -``` - -## Advanced Usage - -`helm init` provides additional flags for modifying Tiller's deployment -manifest before it is installed. - -### Using `--node-selectors` - -The `--node-selectors` flag allows us to specify the node labels required -for scheduling the Tiller pod. - -The example below will create the specified label under the nodeSelector -property. - -``` -helm init --node-selectors "beta.kubernetes.io/os"="linux" -``` - -The installed deployment manifest will contain our node selector label. - -``` -... -spec: - template: - spec: - nodeSelector: - beta.kubernetes.io/os: linux -... -``` - - -### Using `--override` - -`--override` allows you to specify properties of Tiller's -deployment manifest. Unlike the `--set` command used elsewhere in Helm, -`helm init --override` manipulates the specified properties of the final -manifest (there is no "values" file). Therefore you may specify any valid -value for any valid property in the deployment manifest. - -#### Override annotation - -In the example below we use `--override` to add the revision property and set -its value to 1. - -``` -helm init --override metadata.annotations."deployment\.kubernetes\.io/revision"="1" -``` -Output: - -``` -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - annotations: - deployment.kubernetes.io/revision: "1" -... -``` - -#### Override affinity - -In the example below we set properties for node affinity. Multiple -`--override` commands may be combined to modify different properties of the -same list item. - -``` -helm init --override "spec.template.spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution[0].weight"="1" --override "spec.template.spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution[0].preference.matchExpressions[0].key"="e2e-az-name" -``` - -The specified properties are combined into the -"preferredDuringSchedulingIgnoredDuringExecution" property's first -list item. - -``` -... -spec: - strategy: {} - template: - ... - spec: - affinity: - nodeAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - preference: - matchExpressions: - - key: e2e-az-name - operator: "" - weight: 1 -... -``` - -### Using `--output` - -The `--output` flag allows us skip the installation of Tiller's deployment -manifest and simply output the deployment manifest to stdout in either -JSON or YAML format. The output may then be modified with tools like `jq` -and installed manually with `kubectl`. - -In the example below we execute `helm init` with the `--output json` flag. - -``` -helm init --output json -``` - -The Tiller installation is skipped and the manifest is output to stdout -in JSON format. - -``` -"apiVersion": "extensions/v1beta1", -"kind": "Deployment", -"metadata": { - "creationTimestamp": null, - "labels": { - "app": "helm", - "name": "tiller" - }, - "name": "tiller-deploy", - "namespace": "kube-system" -}, -... -``` - -### Storage backends -By default, `tiller` stores release information in `ConfigMaps` in the namespace -where it is running. As of Helm 2.7.0, there is now a beta storage backend that -uses `Secrets` for storing release information. This was added for additional -security in protecting charts in conjunction with the release of `Secret` -encryption in Kubernetes. - -To enable the secrets backend, you'll need to init Tiller with the following -options: - -```shell -helm init --override 'spec.template.spec.containers[0].command'='{/tiller,--storage=secret}' -``` - -Currently, if you want to switch from the default backend to the secrets -backend, you'll have to do the migration for this on your own. When this backend -graduates from beta, there will be a more official path of migration ## Conclusion @@ -352,5 +101,5 @@ In most cases, installation is as simple as getting a pre-built `helm` binary and running `helm init`. This document covers additional cases for those who want to do more sophisticated things with Helm. -Once you have the Helm Client and Tiller successfully installed, you can +Once you have the Helm Client successfully installed, you can move on to using Helm to manage charts. diff --git a/docs/install_faq.md b/docs/install_faq.md index f2eae5b48..d7b6150fa 100644 --- a/docs/install_faq.md +++ b/docs/install_faq.md @@ -35,7 +35,7 @@ Helm. ## Installing -I'm trying to install Helm/Tiller, but something is not right. +I'm trying to install Helm, but something is not right. **Q: How do I put the Helm client files somewhere other than ~/.helm?** @@ -49,53 +49,14 @@ helm init --client-only Note that if you have existing repositories, you will need to re-add them with `helm repo add...`. -**Q: How do I configure Helm, but not install Tiller?** +**Q: How do I configure Helm?** -A: By default, `helm init` will ensure that the local `$HELM_HOME` is configured, -and then install Tiller on your cluster. To locally configure, but not install -Tiller, use `helm init --client-only`. +A: By default, `helm init` will ensure that the local `$HELM_HOME` is configured. -**Q: How do I manually install Tiller on the cluster?** - -A: Tiller is installed as a Kubernetes `deployment`. You can get the manifest -by running `helm init --dry-run --debug`, and then manually install it with -`kubectl`. It is suggested that you do not remove or change the labels on that -deployment, as they are sometimes used by supporting scripts and tools. - -**Q: Why do I get `Error response from daemon: target is unknown` during Tiller install?** - -A: Users have reported being unable to install Tiller on Kubernetes instances that -are using Docker 1.13.0. The root cause of this was a bug in Docker that made -that one version incompatible with images pushed to the Docker registry by -earlier versions of Docker. - -This [issue](https://github.com/docker/docker/issues/30083) was fixed shortly -after the release, and is available in Docker 1.13.1-RC1 and later. ## Getting Started -I successfully installed Helm/Tiller but I can't use it. - -**Q: Trying to use Helm, I get the error "client transport was broken"** - -``` -E1014 02:26:32.885226 16143 portforward.go:329] an error occurred forwarding 37008 -> 44134: error forwarding port 44134 to pod tiller-deploy-2117266891-e4lev_kube-system, uid : unable to do port forwarding: socat not found. -2016/10/14 02:26:32 transport: http2Client.notifyError got notified that the client transport was broken EOF. -Error: transport is closing -``` - -A: This is usually a good indication that Kubernetes is not set up to allow port forwarding. - -Typically, the missing piece is `socat`. If you are running CoreOS, we have been -told that it may have been misconfigured on installation. The CoreOS team -recommends reading this: - -- https://coreos.com/kubernetes/docs/latest/kubelet-wrapper.html - -Here are a few resolved issues that may help you get started: - -- https://github.com/kubernetes/helm/issues/1371 -- https://github.com/kubernetes/helm/issues/966 +I successfully installed Helm but I can't use it. **Q: Trying to use Helm, I get the error "lookup XXXXX on 8.8.8.8:53: no such host"** @@ -136,96 +97,11 @@ certificates and certificate authorities. These need to be stored in a Kubernete config file (Default: `~/.kube/config` so that `kubectl` and `helm` can access them. -**Q: When I run a Helm command, I get an error about the tunnel or proxy** - -A: Helm uses the Kubernetes proxy service to connect to the Tiller server. -If the command `kubectl proxy` does not work for you, neither will Helm. -Typically, the error is related to a missing `socat` service. - -**Q: Tiller crashes with a panic** - -When I run a command on Helm, Tiller crashes with an error like this: - -``` -Tiller is listening on :44134 -Probes server is listening on :44135 -Storage driver is ConfigMap -Cannot initialize Kubernetes connection: the server has asked for the client to provide credentials 2016-12-20 15:18:40.545739 I | storage.go:37: Getting release "bailing-chinchilla" (v1) from storage -panic: runtime error: invalid memory address or nil pointer dereference -[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x8053d5] - -goroutine 77 [running]: -panic(0x1abbfc0, 0xc42000a040) - /usr/local/go/src/runtime/panic.go:500 +0x1a1 -k8s.io/helm/vendor/k8s.io/kubernetes/pkg/client/unversioned.(*ConfigMaps).Get(0xc4200c6200, 0xc420536100, 0x15, 0x1ca7431, 0x6, 0xc42016b6a0) - /home/ubuntu/.go_workspace/src/k8s.io/helm/vendor/k8s.io/kubernetes/pkg/client/unversioned/configmap.go:58 +0x75 -k8s.io/helm/pkg/storage/driver.(*ConfigMaps).Get(0xc4201d6190, 0xc420536100, 0x15, 0xc420536100, 0x15, 0xc4205360c0) - /home/ubuntu/.go_workspace/src/k8s.io/helm/pkg/storage/driver/cfgmaps.go:69 +0x62 -k8s.io/helm/pkg/storage.(*Storage).Get(0xc4201d61a0, 0xc4205360c0, 0x12, 0xc400000001, 0x12, 0x0, 0xc420200070) - /home/ubuntu/.go_workspace/src/k8s.io/helm/pkg/storage/storage.go:38 +0x160 -k8s.io/helm/pkg/tiller.(*ReleaseServer).uniqName(0xc42002a000, 0x0, 0x0, 0xc42016b800, 0xd66a13, 0xc42055a040, 0xc420558050, 0xc420122001) - /home/ubuntu/.go_workspace/src/k8s.io/helm/pkg/tiller/release_server.go:577 +0xd7 -k8s.io/helm/pkg/tiller.(*ReleaseServer).prepareRelease(0xc42002a000, 0xc42027c1e0, 0xc42002a001, 0xc42016bad0, 0xc42016ba08) - /home/ubuntu/.go_workspace/src/k8s.io/helm/pkg/tiller/release_server.go:630 +0x71 -k8s.io/helm/pkg/tiller.(*ReleaseServer).InstallRelease(0xc42002a000, 0x7f284c434068, 0xc420250c00, 0xc42027c1e0, 0x0, 0x31a9, 0x31a9) - /home/ubuntu/.go_workspace/src/k8s.io/helm/pkg/tiller/release_server.go:604 +0x78 -k8s.io/helm/pkg/proto/hapi/services._ReleaseService_InstallRelease_Handler(0x1c51f80, 0xc42002a000, 0x7f284c434068, 0xc420250c00, 0xc42027c190, 0x0, 0x0, 0x0, 0x0, 0x0) - /home/ubuntu/.go_workspace/src/k8s.io/helm/pkg/proto/hapi/services/tiller.pb.go:747 +0x27d -k8s.io/helm/vendor/google.golang.org/grpc.(*Server).processUnaryRPC(0xc4202f3ea0, 0x28610a0, 0xc420078000, 0xc420264690, 0xc420166150, 0x288cbe8, 0xc420250bd0, 0x0, 0x0) - /home/ubuntu/.go_workspace/src/k8s.io/helm/vendor/google.golang.org/grpc/server.go:608 +0xc50 -k8s.io/helm/vendor/google.golang.org/grpc.(*Server).handleStream(0xc4202f3ea0, 0x28610a0, 0xc420078000, 0xc420264690, 0xc420250bd0) - /home/ubuntu/.go_workspace/src/k8s.io/helm/vendor/google.golang.org/grpc/server.go:766 +0x6b0 -k8s.io/helm/vendor/google.golang.org/grpc.(*Server).serveStreams.func1.1(0xc420124710, 0xc4202f3ea0, 0x28610a0, 0xc420078000, 0xc420264690) - /home/ubuntu/.go_workspace/src/k8s.io/helm/vendor/google.golang.org/grpc/server.go:419 +0xab -created by k8s.io/helm/vendor/google.golang.org/grpc.(*Server).serveStreams.func1 - /home/ubuntu/.go_workspace/src/k8s.io/helm/vendor/google.golang.org/grpc/server.go:420 +0xa3 -``` - -A: Check your security settings for Kubernetes. - -A panic in Tiller is almost always the result of a failure to negotiate with the -Kubernetes API server (at which point Tiller can no longer do anything useful, so -it panics and exits). - -Often, this is a result of authentication failing because the Pod in which Tiller -is running does not have the right token. - -To fix this, you will need to change your Kubernetes configuration. Make sure -that `--service-account-private-key-file` from `controller-manager` and -`--service-account-key-file` from apiserver point to the _same_ x509 RSA key. - - -## Upgrading - -My Helm used to work, then I upgrade. Now it is broken. - -**Q: After upgrade, I get the error "Client version is incompatible". What's wrong?** - -Tiller and Helm have to negotiate a common version to make sure that they can safely -communicate without breaking API assumptions. That error means that the version -difference is too great to safely continue. Typically, you need to upgrade -Tiller manually for this. - -The [Installation Guide](install.md) has definitive information about safely -upgrading Helm and Tiller. - -The rules for version numbers are as follows: - -- Pre-release versions are incompatible with everything else. `Alpha.1` is incompatible with `Alpha.2`. -- Patch revisions _are compatible_: 1.2.3 is compatible with 1.2.4 -- Minor revisions _are not compatible_: 1.2.0 is not compatible with 1.3.0, - though we may relax this constraint in the future. -- Major revisions _are not compatible_: 1.0.0 is not compatible with 2.0.0. ## Uninstalling I am trying to remove stuff. -**Q: When I delete the Tiller deployment, how come all the releases are still there?** - -Releases are stored in ConfigMaps inside of the `kube-system` namespace. You will -have to manually delete them to get rid of the record, or use ```helm delete --purge```. - **Q: I want to delete my local Helm. Where are all its files?** Along with the `helm` binary, Helm stores some files in `$HELM_HOME`, which is diff --git a/docs/kubernetes_distros.md b/docs/kubernetes_distros.md index 8b80519ec..c8eac9536 100644 --- a/docs/kubernetes_distros.md +++ b/docs/kubernetes_distros.md @@ -32,20 +32,15 @@ distributions: Some versions of Helm (v2.0.0-beta2) require you to `export KUBECONFIG=/etc/kubernetes/admin.conf` or create a `~/.kube/config`. -## Container Linux by CoreOS - -Helm requires that kubelet have access to a copy of the `socat` program to proxy connections to the Tiller API. On Container Linux the Kubelet runs inside of a [hyperkube](https://github.com/kubernetes/kubernetes/tree/master/cluster/images/hyperkube) container image that has socat. So, even though Container Linux doesn't ship `socat` the container filesystem running kubelet does have socat. To learn more read the [Kubelet Wrapper](https://coreos.com/kubernetes/docs/latest/kubelet-wrapper.html) docs. - ## Openshift Helm works straightforward on OpenShift Online, OpenShift Dedicated, OpenShift Container Platform (version >= 3.6) or OpenShift Origin (version >= 3.6). To learn more read [this blog](https://blog.openshift.com/getting-started-helm-openshift/) post. ## Platform9 -Helm Client and Helm Server (Tiller) are pre-installed with [Platform9 Managed Kubernetes](https://platform9.com/managed-kubernetes/?utm_source=helm_distro_notes). Platform9 provides access to all official Helm charts through the App Catalog UI and native Kubernetes CLI. Additional repositories can be manually added. Further details are available in this [Platform9 App Catalog article](https://platform9.com/support/deploying-kubernetes-apps-platform9-managed-kubernetes/?utm_source=helm_distro_notes). +Helm is pre-installed with [Platform9 Managed Kubernetes](https://platform9.com/managed-kubernetes/?utm_source=helm_distro_notes). Platform9 provides access to all official Helm charts through the App Catalog UI and native Kubernetes CLI. Additional repositories can be manually added. Further details are available in this [Platform9 App Catalog article](https://platform9.com/support/deploying-kubernetes-apps-platform9-managed-kubernetes/?utm_source=helm_distro_notes). ## DC/OS -Helm (both client and server) has been tested and is working on Mesospheres DC/OS 1.11 Kubernetes platform, and requires -no additional configuration. +Helm has been tested and is working on Mesospheres DC/OS 1.11 Kubernetes platform, and requires no additional configuration. diff --git a/docs/plugins.md b/docs/plugins.md index ddce1f288..185257bb8 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -112,7 +112,7 @@ There are some strategies for working with plugin commands: but will not handle `helm myplugin --help`. ## Downloader Plugins -By default, Helm is able to fetch Charts using HTTP/S. As of Helm 2.4.0, plugins +By default, Helm is able to pull Charts using HTTP/S. As of Helm 2.4.0, plugins can have a special capability to download Charts from arbitrary sources. Plugins shall declare this special capability in the `plugin.yaml` file (top level): diff --git a/docs/provenance.md b/docs/provenance.md index 331074e8c..7a3f24fe7 100644 --- a/docs/provenance.md +++ b/docs/provenance.md @@ -76,8 +76,8 @@ $ helm install --verify mychart-0.1.0.tgz If the keyring (containing the public key associated with the signed chart) is not in the default location, you may need to point to the keyring with `--keyring PATH` as in the `helm package` example. -If verification fails, the install will be aborted before the chart is even pushed -up to Tiller. +If verification fails, the install will be aborted before the chart is even rendered. + ### Using Keybase.io credentials diff --git a/docs/quickstart.md b/docs/quickstart.md index 52a7c800f..f6cc5f797 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -8,7 +8,7 @@ The following prerequisites are required for a successful and properly secured u 1. A Kubernetes cluster 2. Deciding what security configurations to apply to your installation, if any -3. Installing and configuring Helm and Tiller, the cluster-side service. +3. Installing and configuring Helm. ### Install Kubernetes or have access to a cluster @@ -17,23 +17,12 @@ The following prerequisites are required for a successful and properly secured u NOTE: Kubernetes versions prior to 1.6 have limited or no support for role-based access controls (RBAC). -Helm will figure out where to install Tiller by reading your Kubernetes -configuration file (usually `$HOME/.kube/config`). This is the same file -that `kubectl` uses. - -To find out which cluster Tiller would install to, you can run -`kubectl config current-context` or `kubectl cluster-info`. - -```console -$ kubectl config current-context -my-cluster -``` ### Understand your Security Context As with all powerful tools, ensure you are installing it correctly for your scenario. -If you're using Helm on a cluster that you completely control, like minikube or a cluster on a private network in which sharing is not a concern, the default installation -- which applies no security configuration -- is fine, and it's definitely the easiest. To install Helm without additional security steps, [install Helm](#Install-Helm) and then [initialize Helm](#initialize-helm-and-install-tiller). +If you're using Helm on a cluster that you completely control, like minikube or a cluster on a private network in which sharing is not a concern, the default installation -- which applies no security configuration -- is fine, and it's definitely the easiest. To install Helm without additional security steps, [install Helm](#Install-Helm) and then [initialize Helm](#initialize-helm). However, if your cluster is exposed to a larger network or if you share your cluster with others -- production clusters fall into this category -- you must take extra steps to secure your installation to prevent careless or malicious actors from damaging the cluster or its data. To apply configurations that secure Helm for use in production environments and other multi-tenant scenarios, see [Securing a Helm installation](securing_installation.md) @@ -48,26 +37,14 @@ Download a binary release of the Helm client. You can use tools like For more details, or for other options, see [the installation guide](install.md). -## Initialize Helm and Install Tiller +## Initialize Helm -Once you have Helm ready, you can initialize the local CLI and also -install Tiller into your Kubernetes cluster in one step: +Once you have Helm ready, you can initialize the local CLI: ```console $ helm init ``` -This will install Tiller into the Kubernetes cluster you saw with -`kubectl config current-context`. - -**TIP:** Want to install into a different cluster? Use the -`--kube-context` flag. - -**TIP:** When you want to upgrade Tiller, just run `helm init --upgrade`. - -By default, when Tiller is installed,it does not have authentication enabled. -To learn more about configuring strong TLS authentication for Tiller, consult -[the Tiller TLS guide](tiller_ssl.md). ## Install an Example Chart @@ -107,10 +84,10 @@ The `helm list` function will show you a list of all deployed releases. ## Uninstall a Release -To uninstall a release, use the `helm delete` command: +To uninstall a release, use the `helm uninstall` command: ```console -$ helm delete smiling-penguin +$ helm uninstall smiling-penguin Removed smiling-penguin ``` @@ -119,11 +96,11 @@ still be able to request information about that release: ```console $ helm status smiling-penguin -Status: DELETED +Status: UNINSTALLED ... ``` -Because Helm tracks your releases even after you've deleted them, you +Because Helm tracks your releases even after you've uninstalled them, you can audit a cluster's history, and even undelete a release (with `helm rollback`). diff --git a/docs/rbac.md b/docs/rbac.md deleted file mode 100644 index 2a3dfe7a0..000000000 --- a/docs/rbac.md +++ /dev/null @@ -1,281 +0,0 @@ -# Role-based Access Control - -In Kubernetes, granting a role to an application-specific service account is a best practice to ensure that your application is operating in the scope that you have specified. Read more about service account permissions [in the official Kubernetes docs](https://kubernetes.io/docs/admin/authorization/rbac/#service-account-permissions). - -Bitnami also has a fantastic guide for [configuring RBAC in your cluster](https://docs.bitnami.com/kubernetes/how-to/configure-rbac-in-your-kubernetes-cluster/) that takes you through RBAC basics. - -This guide is for users who want to restrict Tiller's capabilities to install resources to certain namespaces, or to grant a Helm client running access to a Tiller instance. - -## Tiller and Role-based Access Control - -You can add a service account to Tiller using the `--service-account ` flag while you're configuring Helm. As a prerequisite, you'll have to create a role binding which specifies a [role](https://kubernetes.io/docs/admin/authorization/rbac/#role-and-clusterrole) and a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) name that have been set up in advance. - -Once you have satisfied the pre-requisite and have a service account with the correct permissions, you'll run a command like this: `helm init --service-account ` - -### Example: Service account with cluster-admin role - -```console -$ kubectl create serviceaccount tiller --namespace kube-system -serviceaccount "tiller" created -``` - -In `rbac-config.yaml`: - -```yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: tiller - namespace: kube-system ---- -apiVersion: rbac.authorization.k8s.io/v1beta1 -kind: ClusterRoleBinding -metadata: - name: tiller -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: cluster-admin -subjects: - - kind: ServiceAccount - name: tiller - namespace: kube-system -``` - -_Note: The cluster-admin role is created by default in a Kubernetes cluster, so you don't have to define it explicitly._ - -```console -$ kubectl create -f rbac-config.yaml -serviceaccount "tiller" created -clusterrolebinding "tiller" created -$ helm init --service-account tiller -``` - -### Example: Deploy Tiller in a namespace, restricted to deploying resources only in that namespace - -In the example above, we gave Tiller admin access to the entire cluster. You are not at all required to give Tiller cluster-admin access for it to work. Instead of specifying a ClusterRole or a ClusterRoleBinding, you can specify a Role and RoleBinding to limit Tiller's scope to a particular namespace. - -```console -$ kubectl create namespace tiller-world -namespace "tiller-world" created -$ kubectl create serviceaccount tiller --namespace tiller-world -serviceaccount "tiller" created -``` - -Define a Role that allows Tiller to manage all resources in `tiller-world` like in `role-tiller.yaml`: - -```yaml -kind: Role -apiVersion: rbac.authorization.k8s.io/v1beta1 -metadata: - name: tiller-manager - namespace: tiller-world -rules: -- apiGroups: ["", "extensions", "apps"] - resources: ["*"] - verbs: ["*"] -``` - -```console -$ kubectl create -f role-tiller.yaml -role "tiller-manager" created -``` - -In `rolebinding-tiller.yaml`, - -```yaml -kind: RoleBinding -apiVersion: rbac.authorization.k8s.io/v1beta1 -metadata: - name: tiller-binding - namespace: tiller-world -subjects: -- kind: ServiceAccount - name: tiller - namespace: tiller-world -roleRef: - kind: Role - name: tiller-manager - apiGroup: rbac.authorization.k8s.io -``` - -```console -$ kubectl create -f rolebinding-tiller.yaml -rolebinding "tiller-binding" created -``` - -Afterwards you can run `helm init` to install Tiller in the `tiller-world` namespace. - -```console -$ helm init --service-account tiller --tiller-namespace tiller-world -$HELM_HOME has been configured at /Users/awesome-user/.helm. - -Tiller (the Helm server side component) has been installed into your Kubernetes Cluster. -Happy Helming! - -$ helm install nginx --tiller-namespace tiller-world --namespace tiller-world -NAME: wayfaring-yak -LAST DEPLOYED: Mon Aug 7 16:00:16 2017 -NAMESPACE: tiller-world -STATUS: DEPLOYED - -RESOURCES: -==> v1/Pod -NAME READY STATUS RESTARTS AGE -wayfaring-yak-alpine 0/1 ContainerCreating 0 0s -``` - -### Example: Deploy Tiller in a namespace, restricted to deploying resources in another namespace - -In the example above, we gave Tiller admin access to the namespace it was deployed inside. Now, let's limit Tiller's scope to deploy resources in a different namespace! - -For example, let's install Tiller in the namespace `myorg-system` and allow Tiller to deploy resources in the namespace `myorg-users`. - -```console -$ kubectl create namespace myorg-system -namespace "myorg-system" created -$ kubectl create serviceaccount tiller --namespace myorg-system -serviceaccount "tiller" created -``` - -Define a Role that allows Tiller to manage all resources in `myorg-users` like in `role-tiller.yaml`: - -```yaml -kind: Role -apiVersion: rbac.authorization.k8s.io/v1beta1 -metadata: - name: tiller-manager - namespace: myorg-users -rules: -- apiGroups: ["", "extensions", "apps"] - resources: ["*"] - verbs: ["*"] -``` - -```console -$ kubectl create -f role-tiller.yaml -role "tiller-manager" created -``` - -Bind the service account to that role. In `rolebinding-tiller.yaml`, - -```yaml -kind: RoleBinding -apiVersion: rbac.authorization.k8s.io/v1beta1 -metadata: - name: tiller-binding - namespace: myorg-users -subjects: -- kind: ServiceAccount - name: tiller - namespace: myorg-system -roleRef: - kind: Role - name: tiller-manager - apiGroup: rbac.authorization.k8s.io -``` - -```console -$ kubectl create -f rolebinding-tiller.yaml -rolebinding "tiller-binding" created -``` - -We'll also need to grant Tiller access to read configmaps in myorg-system so it can store release information. In `role-tiller-myorg-system.yaml`: - -```yaml -kind: Role -apiVersion: rbac.authorization.k8s.io/v1beta1 -metadata: - namespace: myorg-system - name: tiller-manager -rules: -- apiGroups: ["", "extensions", "apps"] - resources: ["configmaps"] - verbs: ["*"] -``` - -```console -$ kubectl create -f role-tiller-myorg-system.yaml -role "tiller-manager" created -``` - -And the respective role binding. In `rolebinding-tiller-myorg-system.yaml`: - -```yaml -kind: RoleBinding -apiVersion: rbac.authorization.k8s.io/v1beta1 -metadata: - name: tiller-binding - namespace: myorg-system -subjects: -- kind: ServiceAccount - name: tiller - namespace: myorg-system -roleRef: - kind: Role - name: tiller-manager - apiGroup: rbac.authorization.k8s.io -``` - -```console -$ kubectl create -f rolebinding-tiller-myorg-system.yaml -rolebinding "tiller-binding" created -``` - -## Helm and Role-based Access Control - -When running a Helm client in a pod, in order for the Helm client to talk to a Tiller instance, it will need certain privileges to be granted. Specifically, the Helm client will need to be able to create pods, forward ports and be able to list pods in the namespace where Tiller is running (so it can find Tiller). - -### Example: Deploy Helm in a namespace, talking to Tiller in another namespace - -In this example, we will assume Tiller is running in a namespace called `tiller-world` and that the Helm client is running in a namespace called `helm-world`. By default, Tiller is running in the `kube-system` namespace. - -In `helm-user.yaml`: - -```yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: helm - namespace: helm-world ---- -apiVersion: rbac.authorization.k8s.io/v1beta1 -kind: Role -metadata: - name: tiller-user - namespace: tiller-world -rules: -- apiGroups: - - "" - resources: - - pods/portforward - verbs: - - create -- apiGroups: - - "" - resources: - - pods - verbs: - - list ---- -apiVersion: rbac.authorization.k8s.io/v1beta1 -kind: RoleBinding -metadata: - name: tiller-user-binding - namespace: tiller-world -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: tiller-user -subjects: -- kind: ServiceAccount - name: helm - namespace: helm-world -``` - -```console -$ kubectl create -f helm-user.yaml -serviceaccount "helm" created -role "tiller-user" created -rolebinding "tiller-user-binding" created -``` diff --git a/docs/related.md b/docs/related.md index 997e3f01d..83eb918c2 100644 --- a/docs/related.md +++ b/docs/related.md @@ -25,7 +25,6 @@ or [pull request](https://github.com/kubernetes/helm/pulls). ## Helm Plugins -- [helm-tiller](https://github.com/adamreese/helm-tiller) - Additional commands to work with Tiller - [Technosophos's Helm Plugins](https://github.com/technosophos/helm-plugins) - Plugins for GitHub, Keybase, and GPG - [helm-template](https://github.com/technosophos/helm-template) - Debug/render templates client-side - [Helm Value Store](https://github.com/skuid/helm-value-store) - Plugin for working with Helm deployment values @@ -33,7 +32,6 @@ or [pull request](https://github.com/kubernetes/helm/pulls). - [helm-env](https://github.com/adamreese/helm-env) - Plugin to show current environment - [helm-last](https://github.com/adamreese/helm-last) - Plugin to show the latest release - [helm-nuke](https://github.com/adamreese/helm-nuke) - Plugin to destroy all releases -- [helm-local](https://github.com/adamreese/helm-local) - Plugin to run Tiller as a local daemon - [App Registry](https://github.com/app-registry/helm-plugin) - Plugin to manage charts via the [App Registry specification](https://github.com/app-registry/spec) - [helm-secrets](https://github.com/futuresimple/helm-secrets) - Plugin to manage and store secrets safely - [helm-edit](https://github.com/mstrzele/helm-edit) - Plugin for editing release's values @@ -49,14 +47,12 @@ tag on their plugin repositories. ## Additional Tools -Tools layered on top of Helm or Tiller. +Tools layered on top of Helm. -- [AppsCode Swift](https://github.com/appscode/swift) - Ajax friendly Helm Tiller Proxy using [grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway) - [Quay App Registry](https://coreos.com/blog/quay-application-registry-for-kubernetes.html) - Open Kubernetes application registry, including a Helm access client - [Chartify](https://github.com/appscode/chartify) - Generate Helm charts from existing Kubernetes resources. - [VIM-Kubernetes](https://github.com/andrewstuart/vim-kubernetes) - VIM plugin for Kubernetes and Helm - [Landscaper](https://github.com/Eneco/landscaper/) - "Landscaper takes a set of Helm Chart references with values (a desired state), and realizes this in a Kubernetes cluster." -- [Rudder](https://github.com/AcalephStorage/rudder) - RESTful (JSON) proxy for Tiller's API - [Helmfile](https://github.com/roboll/helmfile) - Helmfile is a declarative spec for deploying helm charts - [Autohelm](https://github.com/reactiveops/autohelm) - Autohelm is _another_ simple declarative spec for deploying helm charts. Written in python and supports git urls as a source for helm charts. - [Helmsman](https://github.com/Praqma/helmsman) - Helmsman is a helm-charts-as-code tool which enables installing/upgrading/protecting/moving/deleting releases from version controlled desired state files (described in a simple TOML format). @@ -67,7 +63,6 @@ Tools layered on top of Helm or Tiller. - [Helm Chart Publisher](https://github.com/luizbafilho/helm-chart-publisher) - HTTP API for publishing Helm Charts in an easy way - [Armada](https://github.com/att-comdev/armada) - Manage prefixed releases throughout various Kubernetes namespaces, and removes completed jobs for complex deployments. Used by the [Openstack-Helm](https://github.com/openstack/openstack-helm) team. - [ChartMuseum](https://github.com/chartmuseum/chartmuseum) - Helm Chart Repository with support for Amazon S3 and Google Cloud Storage -- [Helm.NET](https://github.com/qmfrederik/helm) - A .NET client for Tiller's API - [Codefresh](https://codefresh.io) - Kubernetes native CI/CD and management platform with UI dashboards for managing Helm charts and releases ## Helm Included diff --git a/docs/release_checklist.md b/docs/release_checklist.md index 26506985c..3d8cb15e3 100644 --- a/docs/release_checklist.md +++ b/docs/release_checklist.md @@ -234,8 +234,6 @@ Download Helm X.Y. The common platform binaries are here: - [Linux](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-linux-amd64.tar.gz) - [Windows](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-windows-amd64.tar.gz) -Once you have the client installed, upgrade Tiller with `helm init --upgrade`. - The [Quickstart Guide](https://docs.helm.sh/using_helm/#quickstart-guide) will get you going from there. For **upgrade instructions** or detailed installation notes, check the [install guide](https://docs.helm.sh/using_helm/#installing-helm). You can also use a [script to install](https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get) on any system with `bash`. ## What's Next diff --git a/docs/securing_installation.md b/docs/securing_installation.md deleted file mode 100644 index 5c420242e..000000000 --- a/docs/securing_installation.md +++ /dev/null @@ -1,111 +0,0 @@ -# Securing your Helm Installation - -Helm is a powerful and flexible package-management and operations tool for Kubernetes. Installing it using the default installation command -- `helm init` -- quickly and easily installs **Tiller**, the server-side component with which Helm corresponds. - -This default installation applies **_no security configurations_**, however. It's completely appropriate to use this type of installation when you are working against a cluster with no or very few security concerns, such as local development with Minikube or with a cluster that is well-secured in a private network with no data-sharing or no other users or teams. If this is the case, then the default installation is fine, but remember: With great power comes great responsibility. Always use due diligence when deciding to use the default installation. - -## Who Needs Security Configurations? - -For the following types of clusters we strongly recommend that you apply the proper security configurations to Helm and Tiller to ensure the safety of the cluster, the data in it, and the network to which it is connected. - -- Clusters that are exposed to uncontrolled network environments: either untrusted network actors can access the cluster, or untrusted applications that can access the network environment. -- Clusters that are for many people to use -- _multitenant_ clusters -- as a shared environment -- Clusters that have access to or use high-value data or networks of any type - -Often, environments like these are referred to as _production grade_ or _production quality_ because the damage done to any company by misuse of the cluster can be profound for either customers, the company itself, or both. Once the risk of damage becomes high enough, you need to ensure the integrity of your cluster no matter what the actual risk. - -To configure your installation properly for your environment, you must: - -- Understand the security context of your cluster -- Choose the Best Practices you should apply to your helm installation - -The following assumes you have a Kubernetes configuration file (a _kubeconfig_ file) or one was given to you to access a cluster. - -## Understanding the Security Context of your Cluster - -`helm init` installs Tiller into the cluster in the `kube-system` namespace and without any RBAC rules applied. This is appropriate for local development and other private scenarios because it enables you to be productive immediately. It also enables you to continue running Helm with existing Kubernetes clusters that do not have role-based access control (RBAC) support until you can move your workloads to a more recent Kubernetes version. - -There are four main areas to consider when securing a tiller installation: - -1. Role-based access control, or RBAC -2. Tiller's gRPC endpoint and its usage by Helm -3. Tiller release information -4. Helm charts - -### RBAC - -Recent versions of Kubernetes employ a [role-based access control (or RBAC)](https://en.wikipedia.org/wiki/Role-based_access_control) system (as do modern operating systems) to help mitigate the damage that can done if credentials are misused or bugs exist. Even where an identity is hijacked, the identity has only so many permissions to a controlled space. This effectively adds a layer of security to limit the scope of any attack with that identity. - -Helm and Tiller are designed to install, remove, and modify logical applications that can contain many services interacting together. As a result, often its usefulness involves cluster-wide operations, which in a multitenant cluster means that great care must be taken with access to a cluster-wide Tiller installation to prevent improper activity. - -Specific users and teams -- developers, operators, system and network administrators -- will need their own portion of the cluster in which they can use Helm and Tiller without risking other portions of the cluster. This means using a Kubernetes cluster with RBAC enabled and Tiller configured to enforce them. For more information about using RBAC in Kubernetes, see [Using RBAC Authorization](rbac.md). - -#### Tiller and User Permissions - -Tiller in its current form does not provide a way to map user credentials to specific permissions within Kubernetes. When Tiller is running inside of the cluster, it operates with the permissions of its service account. If no service account name is supplied to Tiller, it runs with the default service account for that namespace. This means that all Tiller operations on that server are executed using the Tiller pod's credentials and permissions. - -To properly limit what Tiller itself can do, the standard Kubernetes RBAC mechanisms must be attached to Tiller, including Roles and RoleBindings that place explicit limits on what things a Tiller instance can install, and where. - -This situation may change in the future. While the community has several methods that might address this, at the moment performing actions using the rights of the client, instead of the rights of Tiller, is contingent upon the outcome of the Pod Identity Working Group, which has taken on the task of solving the problem in a general way. - - -### The Tiller gRPC Endpoint and TLS - -In the default installation the gRPC endpoint that Tiller offers is available inside the cluster (not external to the cluster) without authentication configuration applied. Without applying authentication, any process in the cluster can use the gRPC endpoint to perform operations inside the cluster. In a local or secured private cluster, this enables rapid usage and is normal. (When running outside the cluster, Helm authenticates through the Kubernetes API server to reach Tiller, leveraging existing Kubernetes authentication support.) - -Shared and production clusters -- for the most part -- should use Helm 2.7.2 at a minimum and configure TLS for each Tiller gRPC endpoint to ensure that within the cluster usage of gRPC endpoints is only for the properly authenticated identity for that endpoint. Doing so enables any number of Tiller instances to be deployed in any number of namespaces and yet no unauthenticated usage of any gRPC endpoint is possible. Finally, usa Helm `init` with the `--tiller-tls-verify` option to install Tiller with TLS enabled and to verify remote certificates, and all other Helm commands should use the `--tls` option. - -For more information about the proper steps to configure Tiller and use Helm properly with TLS configured, see [Using SSL between Helm and Tiller](tiller_ssl.md). - -When Helm clients are connecting from outside of the cluster, the security between the Helm client and the API server is managed by Kubernetes itself. You may want to ensure that this link is secure. Note that if you are using the TLS configuration recommended above, not even the Kubernetes API server has access to the unencrypted messages between the client and Tiller. - -### Tiller's Release Information - -For historical reasons, Tiller stores its release information in ConfigMaps. We suggest changing the default to Secrets. - -Secrets are the Kubernetes accepted mechanism for saving configuration data that is considered sensitive. While secrets don't themselves offer many protections, Kubernetes cluster management software often treats them differently than other objects. Thus, we suggest using secrets to store releases. - -Enabling this feature currently requires setting the `--storage=secret` flag in the tiller-deploy deployment. This entails directly modifying the deployment or using `helm init --override=...`, as no helm init flag is currently available to do this for you. For more information, see [Using --override](install.md#using---override). - -### Thinking about Charts - -Because of the relative longevity of Helm, the Helm chart ecosystem evolved without the immediate concern for cluster-wide control, and especially in the developer space this makes complete sense. However, charts are a kind of package that not only installs containers you may or may not have validated yourself, but it may also install into more than one namespace. - -As with all shared software, in a controlled or shared environment you must validate all software you install yourself _before_ you install it. If you have secured Tiller with TLS and have installed it with permissions to only one or a subset of namespaces, some charts may fail to install -- but in these environments, that is exactly what you want. If you need to use the chart, you may have to work with the creator or modify it yourself in order to use it securely in a multitenant cluster with proper RBAC rules applied. The `helm template` command renders the chart locally and displays the output. - -Once vetted, you can use Helm's provenance tools to [ensure the provenance and integrity of charts](provenance.md) that you use. - -### gRPC Tools and Secured Tiller Configurations - -Many very useful tools use the gRPC interface directly, and having been built against the default installation -- which provides cluster-wide access -- may fail once security configurations have been applied. RBAC policies are controlled by you or by the cluster operator, and either can be adjusted for the tool, or the tool can be configured to work properly within the constraints of specific RBAC policies applied to Tiller. The same may need to be done if the gRPC endpoint is secured: the tools need their own secure TLS configuration in order to use a specific Tiller instance. The combination of RBAC policies and a secured gRPC endpoint configured in conjunction with gRPC tools enables you to control your cluster environment as you should. - -## Best Practices for Securing Helm and Tiller - -The following guidelines reiterate the Best Practices for securing Helm and Tiller and using them correctly. - -1. Create a cluster with RBAC enabled -2. Configure each Tiller gRPC endpoint to use a separate TLS certificate -3. Release information should be a Kubernetes Secret -4. Install one Tiller per user, team, or other organizational entity with the `--service-account` flag, Roles, and RoleBindings -5. Use the `--tiller-tls-verify` option with `helm init` and the `--tls` flag with other Helm commands to enforce verification - -If these steps are followed, an example `helm init` command might look something like this: - -```bash -$ helm init \ ---tiller-tls \ ---tiller-tls-verify \ ---tiller-tls-ca-cert=ca.pem \ ---tiller-tls-cert=cert.pem \ ---tiller-tls-key=key.pem \ ---service-account=accountname -``` - -This command will start Tiller with both strong authentication over gRPC, and a service account to which RBAC policies have been applied. - - - - - - - diff --git a/docs/tiller_ssl.md b/docs/tiller_ssl.md deleted file mode 100644 index d7f0166c4..000000000 --- a/docs/tiller_ssl.md +++ /dev/null @@ -1,291 +0,0 @@ -# Using SSL Between Helm and Tiller - -This document explains how to create strong SSL/TLS connections between Helm and -Tiller. The emphasis here is on creating an internal CA, and using both the -cryptographic and identity functions of SSL. - -> Support for TLS-based auth was introduced in Helm 2.3.0 - -Configuring SSL is considered an advanced topic, and knowledge of Helm and Tiller -is assumed. - -## Overview - -The Tiller authentication model uses client-side SSL certificates. Tiller itself -verifies these certificates using a certificate authority. Likewise, the client -also verifies Tiller's identity by certificate authority. - -There are numerous possible configurations for setting up certificates and authorities, -but the method we cover here will work for most situations. - -> As of Helm 2.7.2, Tiller _requires_ that the client certificate be validated -> by its CA. In prior versions, Tiller used a weaker validation strategy that -> allowed self-signed certificates. - -In this guide, we will show how to: - -- Create a private CA that is used to issue certificates for Tiller clients and - servers. -- Create a certificate for Tiller -- Create a certificate for the Helm client -- Create a Tiller instance that uses the certificate -- Configure the Helm client to use the CA and client-side certificate - -By the end of this guide, you should have a Tiller instance running that will -only accept connections from clients who can be authenticated by SSL certificate. - -## Generating Certificate Authorities and Certificates - -One way to generate SSL CAs is via the `openssl` command line tool. There are many -guides and best practices documents available online. This explanation is focused -on getting ready within a small amount of time. For production configurations, -we urge readers to read [the official documentation](https://www.openssl.org) and -consult other resources. - -### Generate a Certificate Authority - -The simplest way to generate a certificate authority is to run two commands: - -```console -$ openssl genrsa -out ./ca.key.pem 4096 -$ openssl req -key ca.key.pem -new -x509 -days 7300 -sha256 -out ca.cert.pem -extensions v3_ca -Enter pass phrase for ca.key.pem: -You are about to be asked to enter information that will be incorporated -into your certificate request. -What you are about to enter is what is called a Distinguished Name or a DN. -There are quite a few fields but you can leave some blank -For some fields there will be a default value, -If you enter '.', the field will be left blank. ------ -Country Name (2 letter code) [AU]:US -State or Province Name (full name) [Some-State]:CO -Locality Name (eg, city) []:Boulder -Organization Name (eg, company) [Internet Widgits Pty Ltd]:tiller -Organizational Unit Name (eg, section) []: -Common Name (e.g. server FQDN or YOUR name) []:tiller -Email Address []:tiller@example.com -``` - -Note that the data input above is _sample data_. You should customize to your own -specifications. - -The above will generate both a secret key and a CA. Note that these two files are -very important. The key in particular should be handled with particular care. - -Often, you will want to generate an intermediate signing key. For the sake of brevity, -we will be signing keys with our root CA. - -### Generating Certificates - -We will be generating two certificates, each representing a type of certificate: - -- One certificate is for Tiller. You will want one of these _per tiller host_ that - you run. -- One certificate is for the user. You will want one of these _per helm user_. - -Since the commands to generate these are the same, we'll be creating both at the -same time. The names will indicate their target. - -First, the Tiller key: - -```console -$ openssl genrsa -out ./tiller.key.pem 4096 -Generating RSA private key, 4096 bit long modulus -..........................................................................................................................................................................................................................................................................................................................++ -............................................................................++ -e is 65537 (0x10001) -Enter pass phrase for ./tiller.key.pem: -Verifying - Enter pass phrase for ./tiller.key.pem: -``` - -Next, generate the Helm client's key: - -```console -$ openssl genrsa -out ./helm.key.pem 4096 -Generating RSA private key, 4096 bit long modulus -.....++ -......................................................................................................................................................................................++ -e is 65537 (0x10001) -Enter pass phrase for ./helm.key.pem: -Verifying - Enter pass phrase for ./helm.key.pem: -``` - -Again, for production use you will generate one client certificate for each user. - -Next we need to create certificates from these keys. For each certificate, this is -a two-step process of creating a CSR, and then creating the certificate. - -```console -$ openssl req -key tiller.key.pem -new -sha256 -out tiller.csr.pem -Enter pass phrase for tiller.key.pem: -You are about to be asked to enter information that will be incorporated -into your certificate request. -What you are about to enter is what is called a Distinguished Name or a DN. -There are quite a few fields but you can leave some blank -For some fields there will be a default value, -If you enter '.', the field will be left blank. ------ -Country Name (2 letter code) [AU]:US -State or Province Name (full name) [Some-State]:CO -Locality Name (eg, city) []:Boulder -Organization Name (eg, company) [Internet Widgits Pty Ltd]:Tiller Server -Organizational Unit Name (eg, section) []: -Common Name (e.g. server FQDN or YOUR name) []:tiller-server -Email Address []: - -Please enter the following 'extra' attributes -to be sent with your certificate request -A challenge password []: -An optional company name []: -``` - -And we repeat this step for the Helm client certificate: - -```console -$ openssl req -key helm.key.pem -new -sha256 -out helm.csr.pem -# Answer the questions with your client user's info -``` - -(In rare cases, we've had to add the `-nodes` flag when generating the request.) - -Now we sign each of these CSRs with the CA certificate we created: - -```console -$ openssl x509 -req -CA ca.cert.pem -CAkey ca.key.pem -CAcreateserial -in tiller.csr.pem -out tiller.cert.pem -Signature ok -subject=/C=US/ST=CO/L=Boulder/O=Tiller Server/CN=tiller-server -Getting CA Private Key -Enter pass phrase for ca.key.pem: -``` - -And again for the client certificate: - -```console -$ openssl x509 -req -CA ca.cert.pem -CAkey ca.key.pem -CAcreateserial -in helm.csr.pem -out helm.cert.pem -``` - -At this point, the important files for us are these: - -``` -# The CA. Make sure the key is kept secret. -ca.cert.pem -ca.key.pem -# The Helm client files -helm.cert.pem -helm.key.pem -# The Tiller server files. -tiller.cert.pem -tiller.key.pem -``` - -Now we're ready to move on to the next steps. - -## Creating a Custom Tiller Installation - -Helm includes full support for creating a deployment configured for SSL. By specifying -a few flags, the `helm init` command can create a new Tiller installation complete -with all of our SSL configuration. - -To take a look at what this will generate, run this command: - -```console -$ helm init --dry-run --debug --tiller-tls --tiller-tls-cert ./tiller.cert.pem --tiller-tls-key ./tiller.key.pem --tiller-tls-verify --tls-ca-cert ca.cert.pem -``` - -The output will show you a Deployment, a Secret, and a Service. Your SSL information -will be preloaded into the Secret, which the Deployment will mount to pods as they -start up. - -If you want to customize the manifest, you can save that output to a file and then -use `kubectl create` to load it into your cluster. - -> We strongly recommend enabling RBAC on your cluster and adding [service accounts](rbac.md) -> with RBAC. - -Otherwise, you can remove the `--dry-run` and `--debug` flags. We also recommend -putting Tiller in a non-system namespace (`--tiller-namespace=something`) and enable -a service account (`--service-account=somename`). But for this example we will stay -with the basics: - -```console -$ helm init --tiller-tls --tiller-tls-cert ./tiller.cert.pem --tiller-tls-key ./tiller.key.pem --tiller-tls-verify --tls-ca-cert ca.cert.pem -``` - -In a minute or two it should be ready. We can check Tiller like this: - -```console -$ kubectl -n kube-system get deployment -NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE -... other stuff -tiller-deploy 1 1 1 1 2m -``` - -If there is a problem, you may want to use `kubectl get pods -n kube-system` to -find out what went wrong. With the SSL/TLS support, the most common problems all -have to do with improperly generated TLS certificates or accidentally swapping the -cert and the key. - -At this point, you should get a _failure_ when you run basic Helm commands: - -```console -$ helm ls -Error: transport is closing -``` - -This is because your Helm client does not have the correct certificate to authenticate -to Tiller. - -## Configuring the Helm Client - -The Tiller server is now running with TLS protection. It's time to configure the -Helm client to also perform TLS operations. - -For a quick test, we can specify our configuration manually. We'll run a normal -Helm command (`helm ls`), but with SSL/TLS enabled. - -```console -helm ls --tls --tls-ca-cert ca.cert.pem --tls-cert helm.cert.pem --tls-key helm.key.pem -``` - -This configuration sends our client-side certificate to establish identity, uses -the client key for encryption, and uses the CA certificate to validate the remote -Tiller's identity. - -Typing a line that is cumbersome, though. The shortcut is to move the key, -cert, and CA into `$HELM_HOME`: - -```console -$ cp ca.cert.pem $(helm home)/ca.pem -$ cp helm.cert.pem $(helm home)/cert.pem -$ cp helm.key.pem $(helm home)/key.pem -``` - -With this, you can simply run `helm ls --tls` to enable TLS. - -### Troubleshooting - -*Running a command, I get `Error: transport is closing`* - -This is almost always due to a configuration error in which the client is missing -a certificate (`--tls-cert`) or the certificate is bad. - -*I'm using a certificate, but get `Error: remote error: tls: bad certificate`* - -This means that Tiller's CA cannot verify your certificate. In the examples above, -we used a single CA to generate both the client and server certificates. In these -examples, the CA has _signed_ the client's certificate. We then load that CA -up to Tiller. So when the client certificate is sent to the server, Tiller -checks the client certificate against the CA. - -*If I use `--tls-verify` on the client, I get `Error: x509: certificate is valid for tiller-server, not localhost`* - -If you plan to use `--tls-verify` on the client, you will need to make sure that -the host name that Helm connects to matches the host name on the certificate. In -some cases this is awkward, since Helm will connect over localhost, or the FQDN is -not available for public resolution. - -## References - -https://github.com/denji/golang-tls -https://www.openssl.org/docs/ -https://jamielinux.com/docs/openssl-certificate-authority/sign-server-and-client-certificates.html diff --git a/docs/using_helm.md b/docs/using_helm.md index 6bf7bc40a..b18466e98 100755 --- a/docs/using_helm.md +++ b/docs/using_helm.md @@ -1,8 +1,8 @@ # Using Helm -This guide explains the basics of using Helm (and Tiller) to manage +This guide explains the basics of using Helm to manage packages on your Kubernetes cluster. It assumes that you have already -[installed](install.md) the Helm client and the Tiller server (typically by `helm +[installed](install.md) the Helm client and library (typically by `helm init`). If you are simply interested in running a few quick commands, you may @@ -387,13 +387,13 @@ is not a full list of cli flags. To see a description of all flags, just run will cause all pods to be recreated (with the exception of pods belonging to deployments) -## 'helm delete': Deleting a Release +## 'helm uninstall': Uninstalling a Release -When it is time to uninstall or delete a release from the cluster, use -the `helm delete` command: +When it is time to uninstall or uninstall a release from the cluster, use +the `helm uninstall` command: ``` -$ helm delete happy-panda +$ helm uninstall happy-panda ``` This will remove the release from the cluster. You can see all of your @@ -406,28 +406,28 @@ inky-cat 1 Wed Sep 28 12:59:46 2016 DEPLOYED alpine-0 ``` From the output above, we can see that the `happy-panda` release was -deleted. +uninstalled. However, Helm always keeps records of what releases happened. Need to -see the deleted releases? `helm list --deleted` shows those, and `helm -list --all` shows all of the releases (deleted and currently deployed, +see the uninstalled releases? `helm list --uninstalled` shows those, and `helm +list --all` shows all of the releases (uninstalled and currently deployed, as well as releases that failed): ```console ⇒ helm list --all NAME VERSION UPDATED STATUS CHART -happy-panda 2 Wed Sep 28 12:47:54 2016 DELETED mariadb-0.3.0 +happy-panda 2 Wed Sep 28 12:47:54 2016 UNINSTALLED mariadb-0.3.0 inky-cat 1 Wed Sep 28 12:59:46 2016 DEPLOYED alpine-0.1.0 -kindred-angelf 2 Tue Sep 27 16:16:10 2016 DELETED alpine-0.1.0 +kindred-angelf 2 Tue Sep 27 16:16:10 2016 UNINSTALLED alpine-0.1.0 ``` -Because Helm keeps records of deleted releases, a release name cannot be -re-used. (If you _really_ need to re-use a release name, you can use the -`--replace` flag, but it will simply re-use the existing release and +Because Helm keeps records of uninstalled releases, a release name cannot +be re-used. (If you _really_ need to re-use a release name, you can use +the `--replace` flag, but it will simply re-use the existing release and replace its resources.) Note that because releases are preserved in this way, you can rollback a -deleted resource, and have it re-activate. +uninstalled resource, and have it re-activate. ## 'helm repo': Working with Repositories @@ -493,19 +493,10 @@ Note: The `stable` repository is managed on the [Kubernetes Charts GitHub repository](https://github.com/kubernetes/charts). That project accepts chart source code, and (after audit) packages those for you. -## Tiller, Namespaces and RBAC -In some cases you may wish to scope Tiller or deploy multiple Tillers to a single cluster. Here are some best practices when operating in those circumstances. - -1. Tiller can be [installed](install.md) into any namespace. By default, it is installed into kube-system. You can run multiple Tillers provided they each run in their own namespace. -2. Limiting Tiller to only be able to install into specific namespaces and/or resource types is controlled by Kubernetes [RBAC](https://kubernetes.io/docs/admin/authorization/rbac/) roles and rolebindings. You can add a service account to Tiller when configuring Helm via `helm init --service-account `. You can find more information about that [here](rbac.md). -3. Release names are unique PER TILLER INSTANCE. -4. Charts should only contain resources that exist in a single namespace. -5. It is not recommended to have multiple Tillers configured to manage resources in the same namespace. - ## Conclusion This chapter has covered the basic usage patterns of the `helm` client, -including searching, installation, upgrading, and deleting. It has also +including searching, installation, upgrading, and uninstalling. It has also covered useful utility commands like `helm status`, `helm get`, and `helm repo`. diff --git a/internal/test/test.go b/internal/test/test.go index 4ddfc9446..3a9e8ec9e 100644 --- a/internal/test/test.go +++ b/internal/test/test.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +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. diff --git a/pkg/chart/chart.go b/pkg/chart/chart.go new file mode 100644 index 000000000..d314104da --- /dev/null +++ b/pkg/chart/chart.go @@ -0,0 +1,95 @@ +/* +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 chart + +// Chart is a helm package that contains metadata, a default config, zero or more +// optionally parameterizable templates, and zero or more charts (dependencies). +type Chart struct { + // Metadata is the contents of the Chartfile. + Metadata *Metadata + // LocK is the contents of Chart.lock. + Lock *Lock + // Templates for this chart. + Templates []*File + // TODO Delete RawValues after unit tests for `create` are refactored. + RawValues []byte + // Values are default config for this template. + Values map[string]interface{} + // Files are miscellaneous files in a chart archive, + // e.g. README, LICENSE, etc. + Files []*File + + parent *Chart + dependencies []*Chart +} + +// SetDependencies replaces the chart dependencies. +func (ch *Chart) SetDependencies(charts ...*Chart) { + ch.dependencies = nil + ch.AddDependency(charts...) +} + +// Name returns the name of the chart. +func (ch *Chart) Name() string { + if ch.Metadata == nil { + return "" + } + return ch.Metadata.Name +} + +// AddDependency determines if the chart is a subchart. +func (ch *Chart) AddDependency(charts ...*Chart) { + for i, x := range charts { + charts[i].parent = ch + ch.dependencies = append(ch.dependencies, x) + } +} + +// Root finds the root chart. +func (ch *Chart) Root() *Chart { + if ch.IsRoot() { + return ch + } + return ch.Parent().Root() +} + +// Dependencies are the charts that this chart depends on. +func (ch *Chart) Dependencies() []*Chart { return ch.dependencies } + +// IsRoot determines if the chart is the root chart. +func (ch *Chart) IsRoot() bool { return ch.parent == nil } + +// Parent returns a subchart's parent chart. +func (ch *Chart) Parent() *Chart { return ch.parent } + +// Parent sets a subchart's parent chart. +func (ch *Chart) SetParent(chart *Chart) { ch.parent = chart } + +// ChartPath returns the full path to this chart in dot notation. +func (ch *Chart) ChartPath() string { + if !ch.IsRoot() { + return ch.Parent().ChartPath() + "." + ch.Name() + } + return ch.Name() +} + +// ChartFullPath returns the full path to this chart. +func (ch *Chart) ChartFullPath() string { + if !ch.IsRoot() { + return ch.Parent().ChartFullPath() + "/charts/" + ch.Name() + } + return ch.Name() +} diff --git a/pkg/chartutil/transform.go b/pkg/chart/chartfile.go similarity index 56% rename from pkg/chartutil/transform.go rename to pkg/chart/chartfile.go index f360e4fad..16ce46b8f 100644 --- a/pkg/chartutil/transform.go +++ b/pkg/chart/chartfile.go @@ -1,11 +1,10 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. - +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 +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, @@ -14,12 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package chartutil - -import "strings" +package chart -// Transform performs a string replacement of the specified source for -// a given key with the replacement string -func Transform(src, key, replacement string) []byte { - return []byte(strings.Replace(src, key, replacement, -1)) -} +// APIVersionv1 is the API version number for version 1. +const APIVersionv1 = "v1" diff --git a/pkg/hapi/chart/file.go b/pkg/chart/file.go similarity index 85% rename from pkg/hapi/chart/file.go rename to pkg/chart/file.go index 90edd59f1..45f64efe8 100644 --- a/pkg/hapi/chart/file.go +++ b/pkg/chart/file.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors All rights reserved. +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 @@ -21,7 +21,7 @@ package chart // base directory. type File struct { // Name is the path-like name of the template. - Name string `json:"name,omitempty"` + Name string // Data is the template as byte data. - Data []byte `json:"data,omitempty"` + Data []byte } diff --git a/pkg/chart/loader/archive.go b/pkg/chart/loader/archive.go new file mode 100644 index 000000000..82f7572e0 --- /dev/null +++ b/pkg/chart/loader/archive.go @@ -0,0 +1,110 @@ +/* +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 loader + +import ( + "archive/tar" + "bytes" + "compress/gzip" + "io" + "os" + "strings" + + "github.com/pkg/errors" + + "k8s.io/helm/pkg/chart" +) + +type FileLoader string + +func (l FileLoader) Load() (*chart.Chart, error) { + return LoadFile(string(l)) +} + +// LoadFile loads from an archive file. +func LoadFile(name string) (*chart.Chart, error) { + if fi, err := os.Stat(name); err != nil { + return nil, err + } else if fi.IsDir() { + return nil, errors.New("cannot load a directory") + } + + raw, err := os.Open(name) + if err != nil { + return nil, err + } + defer raw.Close() + + return LoadArchive(raw) +} + +// LoadArchive loads from a reader containing a compressed tar archive. +func LoadArchive(in io.Reader) (*chart.Chart, error) { + unzipped, err := gzip.NewReader(in) + if err != nil { + return &chart.Chart{}, err + } + defer unzipped.Close() + + files := []*BufferedFile{} + tr := tar.NewReader(unzipped) + for { + b := bytes.NewBuffer(nil) + hd, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + return &chart.Chart{}, err + } + + if hd.FileInfo().IsDir() { + // Use this instead of hd.Typeflag because we don't have to do any + // inference chasing. + continue + } + + // Archive could contain \ if generated on Windows + delimiter := "/" + if strings.ContainsRune(hd.Name, '\\') { + delimiter = "\\" + } + + parts := strings.Split(hd.Name, delimiter) + n := strings.Join(parts[1:], delimiter) + + // Normalize the path to the / delimiter + n = strings.Replace(n, delimiter, "/", -1) + + if parts[0] == "Chart.yaml" { + return nil, errors.New("chart yaml not in base directory") + } + + if _, err := io.Copy(b, tr); err != nil { + return &chart.Chart{}, err + } + + files = append(files, &BufferedFile{Name: n, Data: b.Bytes()}) + b.Reset() + } + + if len(files) == 0 { + return nil, errors.New("no files in chart archive") + } + + return LoadFiles(files) +} diff --git a/pkg/chart/loader/directory.go b/pkg/chart/loader/directory.go new file mode 100644 index 000000000..1e3f4e4af --- /dev/null +++ b/pkg/chart/loader/directory.go @@ -0,0 +1,105 @@ +/* +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 loader + +import ( + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/pkg/errors" + + "k8s.io/helm/pkg/chart" + "k8s.io/helm/pkg/ignore" + "k8s.io/helm/pkg/sympath" +) + +type DirLoader string + +func (l DirLoader) Load() (*chart.Chart, error) { + return LoadDir(string(l)) +} + +// LoadDir loads from a directory. +// +// This loads charts only from directories. +func LoadDir(dir string) (*chart.Chart, error) { + topdir, err := filepath.Abs(dir) + if err != nil { + return nil, err + } + + // Just used for errors. + c := &chart.Chart{} + + rules := ignore.Empty() + ifile := filepath.Join(topdir, ignore.HelmIgnore) + if _, err := os.Stat(ifile); err == nil { + r, err := ignore.ParseFile(ifile) + if err != nil { + return c, err + } + rules = r + } + rules.AddDefaults() + + files := []*BufferedFile{} + topdir += string(filepath.Separator) + + walk := func(name string, fi os.FileInfo, err error) error { + n := strings.TrimPrefix(name, topdir) + if n == "" { + // No need to process top level. Avoid bug with helmignore .* matching + // empty names. See issue 1779. + return nil + } + + // Normalize to / since it will also work on Windows + n = filepath.ToSlash(n) + + if err != nil { + return err + } + if fi.IsDir() { + // Directory-based ignore rules should involve skipping the entire + // contents of that directory. + if rules.Ignore(n, fi) { + return filepath.SkipDir + } + return nil + } + + // If a .helmignore file matches, skip this file. + if rules.Ignore(n, fi) { + return nil + } + + data, err := ioutil.ReadFile(name) + if err != nil { + return errors.Wrapf(err, "error reading %s", n) + } + + files = append(files, &BufferedFile{Name: n, Data: data}) + return nil + } + if err = sympath.Walk(topdir, walk); err != nil { + return c, err + } + + return LoadFiles(files) +} diff --git a/pkg/chart/loader/load.go b/pkg/chart/loader/load.go new file mode 100644 index 000000000..855d92e57 --- /dev/null +++ b/pkg/chart/loader/load.go @@ -0,0 +1,150 @@ +/* +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 loader + +import ( + "bytes" + "os" + "path/filepath" + "strings" + + "github.com/ghodss/yaml" + "github.com/pkg/errors" + + "k8s.io/helm/pkg/chart" +) + +type ChartLoader interface { + Load() (*chart.Chart, error) +} + +func Loader(name string) (ChartLoader, error) { + fi, err := os.Stat(name) + if err != nil { + return nil, err + } + if fi.IsDir() { + return DirLoader(name), nil + } + return FileLoader(name), nil + +} + +// Load takes a string name, tries to resolve it to a file or directory, and then loads it. +// +// This is the preferred way to load a chart. It will discover the chart encoding +// and hand off to the appropriate chart reader. +// +// If a .helmignore file is present, the directory loader will skip loading any files +// matching it. But .helmignore is not evaluated when reading out of an archive. +func Load(name string) (*chart.Chart, error) { + l, err := Loader(name) + if err != nil { + return nil, err + } + return l.Load() +} + +// BufferedFile represents an archive file buffered for later processing. +type BufferedFile struct { + Name string + Data []byte +} + +// LoadFiles loads from in-memory files. +func LoadFiles(files []*BufferedFile) (*chart.Chart, error) { + c := new(chart.Chart) + subcharts := make(map[string][]*BufferedFile) + + for _, f := range files { + switch { + case f.Name == "Chart.yaml": + c.Metadata = new(chart.Metadata) + if err := yaml.Unmarshal(f.Data, c.Metadata); err != nil { + return c, errors.Wrap(err, "cannot load Chart.yaml") + } + case f.Name == "Chart.lock": + c.Lock = new(chart.Lock) + if err := yaml.Unmarshal(f.Data, &c.Lock); err != nil { + return c, errors.Wrap(err, "cannot load Chart.lock") + } + case f.Name == "values.yaml": + c.Values = make(map[string]interface{}) + if err := yaml.Unmarshal(f.Data, &c.Values); err != nil { + return c, errors.Wrap(err, "cannot load values.yaml") + } + c.RawValues = f.Data + case strings.HasPrefix(f.Name, "templates/"): + c.Templates = append(c.Templates, &chart.File{Name: f.Name, Data: f.Data}) + case strings.HasPrefix(f.Name, "charts/"): + if filepath.Ext(f.Name) == ".prov" { + c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data}) + continue + } + + fname := strings.TrimPrefix(f.Name, "charts/") + cname := strings.SplitN(fname, "/", 2)[0] + subcharts[cname] = append(subcharts[cname], &BufferedFile{Name: fname, Data: f.Data}) + default: + c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data}) + } + } + + // Ensure that we got a Chart.yaml file + if c.Metadata == nil { + return c, errors.New("chart metadata (Chart.yaml) missing") + } + if c.Name() == "" { + return c, errors.New("invalid chart (Chart.yaml): name must not be empty") + } + + for n, files := range subcharts { + var sc *chart.Chart + var err error + switch { + case strings.IndexAny(n, "_.") == 0: + continue + case filepath.Ext(n) == ".tgz": + file := files[0] + if file.Name != n { + return c, errors.Errorf("error unpacking tar in %s: expected %s, got %s", c.Name(), n, file.Name) + } + // Untar the chart and add to c.Dependencies + sc, err = LoadArchive(bytes.NewBuffer(file.Data)) + default: + // We have to trim the prefix off of every file, and ignore any file + // that is in charts/, but isn't actually a chart. + buff := make([]*BufferedFile, 0, len(files)) + for _, f := range files { + parts := strings.SplitN(f.Name, "/", 2) + if len(parts) < 2 { + continue + } + f.Name = parts[1] + buff = append(buff, f) + } + sc, err = LoadFiles(buff) + } + + if err != nil { + return c, errors.Wrapf(err, "error unpacking %s in %s", n, c.Name()) + } + c.AddDependency(sc) + } + + return c, nil +} diff --git a/pkg/chartutil/load_test.go b/pkg/chart/loader/load_test.go similarity index 63% rename from pkg/chartutil/load_test.go rename to pkg/chart/loader/load_test.go index 36dc37185..6fda07ceb 100644 --- a/pkg/chartutil/load_test.go +++ b/pkg/chart/loader/load_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -14,27 +14,35 @@ See the License for the specific language governing permissions and limitations under the License. */ -package chartutil +package loader import ( - "path" "testing" - "k8s.io/helm/pkg/hapi/chart" + "k8s.io/helm/pkg/chart" ) func TestLoadDir(t *testing.T) { - c, err := Load("testdata/frobnitz") + l, err := Loader("testdata/frobnitz") + if err != nil { + t.Fatalf("Failed to load testdata: %s", err) + } + c, err := l.Load() if err != nil { t.Fatalf("Failed to load testdata: %s", err) } verifyFrobnitz(t, c) verifyChart(t, c) verifyRequirements(t, c) + verifyRequirementsLock(t, c) } func TestLoadFile(t *testing.T) { - c, err := Load("testdata/frobnitz-1.2.3.tgz") + l, err := Loader("testdata/frobnitz-1.2.3.tgz") + if err != nil { + t.Fatalf("Failed to load testdata: %s", err) + } + c, err := l.Load() if err != nil { t.Fatalf("Failed to load testdata: %s", err) } @@ -46,7 +54,7 @@ func TestLoadFile(t *testing.T) { func TestLoadFiles(t *testing.T) { goodFiles := []*BufferedFile{ { - Name: ChartfileName, + Name: "Chart.yaml", Data: []byte(`apiVersion: v1 name: frobnitz description: This is a frobnitz. @@ -67,16 +75,16 @@ icon: https://example.com/64x64.png `), }, { - Name: ValuesfileName, - Data: []byte(defaultValues), + Name: "values.yaml", + Data: []byte("var: some values"), }, { - Name: path.Join("templates", DeploymentName), - Data: []byte(defaultDeployment), + Name: "templates/deployment.yaml", + Data: []byte("some deployment"), }, { - Name: path.Join("templates", ServiceName), - Data: []byte(defaultService), + Name: "templates/service.yaml", + Data: []byte("some service"), }, } @@ -85,11 +93,11 @@ icon: https://example.com/64x64.png t.Errorf("Expected good files to be loaded, got %v", err) } - if c.Metadata.Name != "frobnitz" { - t.Errorf("Expected chart name to be 'frobnitz', got %s", c.Metadata.Name) + if c.Name() != "frobnitz" { + t.Errorf("Expected chart name to be 'frobnitz', got %s", c.Name()) } - if string(c.Values) != defaultValues { + if c.Values["var"] != "some values" { t.Error("Expected chart values to be populated with default values") } @@ -119,15 +127,16 @@ func TestLoadFileBackslash(t *testing.T) { } func verifyChart(t *testing.T, c *chart.Chart) { - if c.Metadata.Name == "" { + t.Helper() + if c.Name() == "" { t.Fatalf("No chart metadata found on %v", c) } - t.Logf("Verifying chart %s", c.Metadata.Name) + t.Logf("Verifying chart %s", c.Name()) if len(c.Templates) != 1 { t.Errorf("Expected 1 template, got %d", len(c.Templates)) } - numfiles := 8 + numfiles := 6 if len(c.Files) != numfiles { t.Errorf("Expected %d extra files, got %d", numfiles, len(c.Files)) for _, n := range c.Files { @@ -135,10 +144,10 @@ func verifyChart(t *testing.T, c *chart.Chart) { } } - if len(c.Dependencies) != 2 { - t.Errorf("Expected 2 dependencies, got %d (%v)", len(c.Dependencies), c.Dependencies) - for _, d := range c.Dependencies { - t.Logf("\tSubchart: %s\n", d.Metadata.Name) + if len(c.Dependencies()) != 2 { + t.Errorf("Expected 2 dependencies, got %d (%v)", len(c.Dependencies()), c.Dependencies()) + for _, d := range c.Dependencies() { + t.Logf("\tSubchart: %s\n", d.Name()) } } @@ -151,35 +160,31 @@ func verifyChart(t *testing.T, c *chart.Chart) { }, } - for _, dep := range c.Dependencies { + for _, dep := range c.Dependencies() { if dep.Metadata == nil { t.Fatalf("expected metadata on dependency: %v", dep) } - exp, ok := expect[dep.Metadata.Name] + exp, ok := expect[dep.Name()] if !ok { - t.Fatalf("Unknown dependency %s", dep.Metadata.Name) + t.Fatalf("Unknown dependency %s", dep.Name()) } if exp["version"] != dep.Metadata.Version { - t.Errorf("Expected %s version %s, got %s", dep.Metadata.Name, exp["version"], dep.Metadata.Version) + t.Errorf("Expected %s version %s, got %s", dep.Name(), exp["version"], dep.Metadata.Version) } } } func verifyRequirements(t *testing.T, c *chart.Chart) { - r, err := LoadRequirements(c) - if err != nil { - t.Fatal(err) - } - if len(r.Dependencies) != 2 { - t.Errorf("Expected 2 requirements, got %d", len(r.Dependencies)) + if len(c.Metadata.Requirements) != 2 { + t.Errorf("Expected 2 requirements, got %d", len(c.Metadata.Requirements)) } - tests := []*Dependency{ + tests := []*chart.Dependency{ {Name: "alpine", Version: "0.1.0", Repository: "https://example.com/charts"}, {Name: "mariner", Version: "4.3.2", Repository: "https://example.com/charts"}, } for i, tt := range tests { - d := r.Dependencies[i] + d := c.Metadata.Requirements[i] if d.Name != tt.Name { t.Errorf("Expected dependency named %q, got %q", tt.Name, d.Name) } @@ -191,20 +196,17 @@ func verifyRequirements(t *testing.T, c *chart.Chart) { } } } + func verifyRequirementsLock(t *testing.T, c *chart.Chart) { - r, err := LoadRequirementsLock(c) - if err != nil { - t.Fatal(err) - } - if len(r.Dependencies) != 2 { - t.Errorf("Expected 2 requirements, got %d", len(r.Dependencies)) + if len(c.Metadata.Requirements) != 2 { + t.Errorf("Expected 2 requirements, got %d", len(c.Metadata.Requirements)) } - tests := []*Dependency{ + tests := []*chart.Dependency{ {Name: "alpine", Version: "0.1.0", Repository: "https://example.com/charts"}, {Name: "mariner", Version: "4.3.2", Repository: "https://example.com/charts"}, } for i, tt := range tests { - d := r.Dependencies[i] + d := c.Metadata.Requirements[i] if d.Name != tt.Name { t.Errorf("Expected dependency named %q, got %q", tt.Name, d.Name) } @@ -222,18 +224,55 @@ func verifyFrobnitz(t *testing.T, c *chart.Chart) { } func verifyChartFileAndTemplate(t *testing.T, c *chart.Chart, name string) { - - verifyChartfile(t, c.Metadata, name) - + if c.Metadata == nil { + t.Fatal("Metadata is nil") + } + if c.Name() != name { + t.Errorf("Expected %s, got %s", name, c.Name()) + } if len(c.Templates) != 1 { t.Fatalf("Expected 1 template, got %d", len(c.Templates)) } - if c.Templates[0].Name != "templates/template.tpl" { t.Errorf("Unexpected template: %s", c.Templates[0].Name) } - if len(c.Templates[0].Data) == 0 { t.Error("No template data.") } + if len(c.Files) != 6 { + t.Fatalf("Expected 6 Files, got %d", len(c.Files)) + } + if len(c.Dependencies()) != 2 { + t.Fatalf("Expected 2 Dependency, got %d", len(c.Dependencies())) + } + if len(c.Metadata.Requirements) != 2 { + t.Fatalf("Expected 2 Requirements.Dependency, got %d", len(c.Metadata.Requirements)) + } + if len(c.Lock.Dependencies) != 2 { + t.Fatalf("Expected 2 Lock.Dependency, got %d", len(c.Lock.Dependencies)) + } + + for _, dep := range c.Dependencies() { + switch dep.Name() { + case "mariner": + case "alpine": + if len(dep.Templates) != 1 { + t.Fatalf("Expected 1 template, got %d", len(dep.Templates)) + } + if dep.Templates[0].Name != "templates/alpine-pod.yaml" { + t.Errorf("Unexpected template: %s", dep.Templates[0].Name) + } + if len(dep.Templates[0].Data) == 0 { + t.Error("No template data.") + } + if len(dep.Files) != 1 { + t.Fatalf("Expected 1 Files, got %d", len(dep.Files)) + } + if len(dep.Dependencies()) != 2 { + t.Fatalf("Expected 2 Dependency, got %d", len(dep.Dependencies())) + } + default: + t.Errorf("Unexpected dependeny %s", dep.Name()) + } + } } diff --git a/pkg/chart/loader/testdata/albatross/Chart.yaml b/pkg/chart/loader/testdata/albatross/Chart.yaml new file mode 100644 index 000000000..eeef737ff --- /dev/null +++ b/pkg/chart/loader/testdata/albatross/Chart.yaml @@ -0,0 +1,4 @@ +name: albatross +description: A Helm chart for Kubernetes +version: 0.1.0 +home: "" diff --git a/pkg/chart/loader/testdata/albatross/values.yaml b/pkg/chart/loader/testdata/albatross/values.yaml new file mode 100644 index 000000000..3121cd7ce --- /dev/null +++ b/pkg/chart/loader/testdata/albatross/values.yaml @@ -0,0 +1,4 @@ +albatross: "true" + +global: + author: Coleridge diff --git a/pkg/chart/loader/testdata/frobnitz-1.2.3.tgz b/pkg/chart/loader/testdata/frobnitz-1.2.3.tgz new file mode 100644 index 000000000..983820e06 Binary files /dev/null and b/pkg/chart/loader/testdata/frobnitz-1.2.3.tgz differ diff --git a/pkg/chart/loader/testdata/frobnitz/.helmignore b/pkg/chart/loader/testdata/frobnitz/.helmignore new file mode 100644 index 000000000..9973a57b8 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz/.helmignore @@ -0,0 +1 @@ +ignore/ diff --git a/pkg/chartutil/testdata/dependent-chart-alias/requirements.lock b/pkg/chart/loader/testdata/frobnitz/Chart.lock similarity index 100% rename from pkg/chartutil/testdata/dependent-chart-alias/requirements.lock rename to pkg/chart/loader/testdata/frobnitz/Chart.lock diff --git a/pkg/chart/loader/testdata/frobnitz/Chart.yaml b/pkg/chart/loader/testdata/frobnitz/Chart.yaml new file mode 100644 index 000000000..fcd4a4a37 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz/Chart.yaml @@ -0,0 +1,27 @@ +apiVersion: v1 +name: frobnitz +description: This is a frobnitz. +version: "1.2.3" +keywords: + - frobnitz + - sprocket + - dodad +maintainers: + - name: The Helm Team + email: helm@example.com + - name: Someone Else + email: nobody@example.com +sources: + - https://example.com/foo/bar +home: http://example.com +icon: https://example.com/64x64.png +annotations: + extrakey: extravalue + anotherkey: anothervalue +dependencies: + - name: alpine + version: "0.1.0" + repository: https://example.com/charts + - name: mariner + version: "4.3.2" + repository: https://example.com/charts diff --git a/pkg/chart/loader/testdata/frobnitz/INSTALL.txt b/pkg/chart/loader/testdata/frobnitz/INSTALL.txt new file mode 100644 index 000000000..2010438c2 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz/INSTALL.txt @@ -0,0 +1 @@ +This is an install document. The client may display this. diff --git a/pkg/chart/loader/testdata/frobnitz/LICENSE b/pkg/chart/loader/testdata/frobnitz/LICENSE new file mode 100644 index 000000000..6121943b1 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz/LICENSE @@ -0,0 +1 @@ +LICENSE placeholder. diff --git a/pkg/chart/loader/testdata/frobnitz/README.md b/pkg/chart/loader/testdata/frobnitz/README.md new file mode 100644 index 000000000..8cf4cc3d7 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz/README.md @@ -0,0 +1,11 @@ +# Frobnitz + +This is an example chart. + +## Usage + +This is an example. It has no usage. + +## Development + +For developer info, see the top-level repository. diff --git a/pkg/chart/loader/testdata/frobnitz/charts/_ignore_me b/pkg/chart/loader/testdata/frobnitz/charts/_ignore_me new file mode 100644 index 000000000..2cecca682 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz/charts/_ignore_me @@ -0,0 +1 @@ +This should be ignored by the loader, but may be included in a chart. diff --git a/pkg/chart/loader/testdata/frobnitz/charts/alpine/Chart.yaml b/pkg/chart/loader/testdata/frobnitz/charts/alpine/Chart.yaml new file mode 100644 index 000000000..38a4aaa54 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz/charts/alpine/Chart.yaml @@ -0,0 +1,4 @@ +name: alpine +description: Deploy a basic Alpine Linux pod +version: 0.1.0 +home: https://k8s.io/helm diff --git a/pkg/chart/loader/testdata/frobnitz/charts/alpine/README.md b/pkg/chart/loader/testdata/frobnitz/charts/alpine/README.md new file mode 100644 index 000000000..a7c84fc41 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz/charts/alpine/README.md @@ -0,0 +1,9 @@ +This example was generated using the command `helm create alpine`. + +The `templates/` directory contains a very simple pod resource with a +couple of parameters. + +The `values.toml` file contains the default values for the +`alpine-pod.yaml` template. + +You can install this example using `helm install docs/examples/alpine`. diff --git a/pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast1/Chart.yaml b/pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast1/Chart.yaml new file mode 100644 index 000000000..171e36156 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast1/Chart.yaml @@ -0,0 +1,4 @@ +name: mast1 +description: A Helm chart for Kubernetes +version: 0.1.0 +home: "" diff --git a/pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast1/values.yaml b/pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast1/values.yaml new file mode 100644 index 000000000..42c39c262 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast1/values.yaml @@ -0,0 +1,4 @@ +# Default values for mast1. +# This is a YAML-formatted file. +# Declare name/value pairs to be passed into your templates. +# name = "value" diff --git a/pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast2-0.1.0.tgz new file mode 100644 index 000000000..ced5a4a6a Binary files /dev/null and b/pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast2-0.1.0.tgz differ diff --git a/pkg/chart/loader/testdata/frobnitz/charts/alpine/templates/alpine-pod.yaml b/pkg/chart/loader/testdata/frobnitz/charts/alpine/templates/alpine-pod.yaml new file mode 100644 index 000000000..0c6980cf7 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz/charts/alpine/templates/alpine-pod.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Pod +metadata: + name: {{.Release.Name}}-{{.Chart.Name}} + labels: + heritage: {{.Release.Service}} + chartName: {{.Chart.Name}} + chartVersion: {{.Chart.Version | quote}} +spec: + restartPolicy: {{default "Never" .restart_policy}} + containers: + - name: waiter + image: "alpine:3.3" + command: ["/bin/sleep","9000"] diff --git a/pkg/chart/loader/testdata/frobnitz/charts/alpine/values.yaml b/pkg/chart/loader/testdata/frobnitz/charts/alpine/values.yaml new file mode 100644 index 000000000..6c2aab7ba --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz/charts/alpine/values.yaml @@ -0,0 +1,2 @@ +# The pod name +name: "my-alpine" diff --git a/pkg/chart/loader/testdata/frobnitz/charts/mariner-4.3.2.tgz b/pkg/chart/loader/testdata/frobnitz/charts/mariner-4.3.2.tgz new file mode 100644 index 000000000..88c24d822 Binary files /dev/null and b/pkg/chart/loader/testdata/frobnitz/charts/mariner-4.3.2.tgz differ diff --git a/pkg/chart/loader/testdata/frobnitz/docs/README.md b/pkg/chart/loader/testdata/frobnitz/docs/README.md new file mode 100644 index 000000000..d40747caf --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz/docs/README.md @@ -0,0 +1 @@ +This is a placeholder for documentation. diff --git a/pkg/chart/loader/testdata/frobnitz/icon.svg b/pkg/chart/loader/testdata/frobnitz/icon.svg new file mode 100644 index 000000000..892130606 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz/icon.svg @@ -0,0 +1,8 @@ + + + Example icon + + + diff --git a/pkg/chart/loader/testdata/frobnitz/ignore/me.txt b/pkg/chart/loader/testdata/frobnitz/ignore/me.txt new file mode 100644 index 000000000..e69de29bb diff --git a/pkg/chart/loader/testdata/frobnitz/templates/template.tpl b/pkg/chart/loader/testdata/frobnitz/templates/template.tpl new file mode 100644 index 000000000..c651ee6a0 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz/templates/template.tpl @@ -0,0 +1 @@ +Hello {{.Name | default "world"}} diff --git a/pkg/chart/loader/testdata/frobnitz/values.yaml b/pkg/chart/loader/testdata/frobnitz/values.yaml new file mode 100644 index 000000000..61f501258 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz/values.yaml @@ -0,0 +1,6 @@ +# A values file contains configuration. + +name: "Some Name" + +section: + name: "Name in a section" diff --git a/pkg/chart/loader/testdata/frobnitz_backslash-1.2.3.tgz b/pkg/chart/loader/testdata/frobnitz_backslash-1.2.3.tgz new file mode 100644 index 000000000..513bfca1a Binary files /dev/null and b/pkg/chart/loader/testdata/frobnitz_backslash-1.2.3.tgz differ diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/.helmignore b/pkg/chart/loader/testdata/frobnitz_backslash/.helmignore new file mode 100755 index 000000000..9973a57b8 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_backslash/.helmignore @@ -0,0 +1 @@ +ignore/ diff --git a/pkg/chartutil/testdata/frobnitz/requirements.lock b/pkg/chart/loader/testdata/frobnitz_backslash/Chart.lock old mode 100644 new mode 100755 similarity index 100% rename from pkg/chartutil/testdata/frobnitz/requirements.lock rename to pkg/chart/loader/testdata/frobnitz_backslash/Chart.lock diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/Chart.yaml b/pkg/chart/loader/testdata/frobnitz_backslash/Chart.yaml new file mode 100755 index 000000000..b1dd40a5d --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_backslash/Chart.yaml @@ -0,0 +1,27 @@ +apiVersion: v1 +name: frobnitz_backslash +description: This is a frobnitz. +version: "1.2.3" +keywords: + - frobnitz + - sprocket + - dodad +maintainers: + - name: The Helm Team + email: helm@example.com + - name: Someone Else + email: nobody@example.com +sources: + - https://example.com/foo/bar +home: http://example.com +icon: https://example.com/64x64.png +annotations: + extrakey: extravalue + anotherkey: anothervalue +dependencies: + - name: alpine + version: "0.1.0" + repository: https://example.com/charts + - name: mariner + version: "4.3.2" + repository: https://example.com/charts diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/INSTALL.txt b/pkg/chart/loader/testdata/frobnitz_backslash/INSTALL.txt new file mode 100755 index 000000000..2010438c2 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_backslash/INSTALL.txt @@ -0,0 +1 @@ +This is an install document. The client may display this. diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/LICENSE b/pkg/chart/loader/testdata/frobnitz_backslash/LICENSE new file mode 100755 index 000000000..6121943b1 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_backslash/LICENSE @@ -0,0 +1 @@ +LICENSE placeholder. diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/README.md b/pkg/chart/loader/testdata/frobnitz_backslash/README.md new file mode 100755 index 000000000..8cf4cc3d7 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_backslash/README.md @@ -0,0 +1,11 @@ +# Frobnitz + +This is an example chart. + +## Usage + +This is an example. It has no usage. + +## Development + +For developer info, see the top-level repository. diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/charts/_ignore_me b/pkg/chart/loader/testdata/frobnitz_backslash/charts/_ignore_me new file mode 100755 index 000000000..2cecca682 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_backslash/charts/_ignore_me @@ -0,0 +1 @@ +This should be ignored by the loader, but may be included in a chart. diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/Chart.yaml b/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/Chart.yaml new file mode 100755 index 000000000..38a4aaa54 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/Chart.yaml @@ -0,0 +1,4 @@ +name: alpine +description: Deploy a basic Alpine Linux pod +version: 0.1.0 +home: https://k8s.io/helm diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/README.md b/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/README.md new file mode 100755 index 000000000..a7c84fc41 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/README.md @@ -0,0 +1,9 @@ +This example was generated using the command `helm create alpine`. + +The `templates/` directory contains a very simple pod resource with a +couple of parameters. + +The `values.toml` file contains the default values for the +`alpine-pod.yaml` template. + +You can install this example using `helm install docs/examples/alpine`. diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast1/Chart.yaml b/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast1/Chart.yaml new file mode 100755 index 000000000..171e36156 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast1/Chart.yaml @@ -0,0 +1,4 @@ +name: mast1 +description: A Helm chart for Kubernetes +version: 0.1.0 +home: "" diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast1/values.yaml b/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast1/values.yaml new file mode 100755 index 000000000..42c39c262 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast1/values.yaml @@ -0,0 +1,4 @@ +# Default values for mast1. +# This is a YAML-formatted file. +# Declare name/value pairs to be passed into your templates. +# name = "value" diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast2-0.1.0.tgz new file mode 100755 index 000000000..ced5a4a6a Binary files /dev/null and b/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast2-0.1.0.tgz differ diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/templates/alpine-pod.yaml b/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/templates/alpine-pod.yaml new file mode 100755 index 000000000..0c6980cf7 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/templates/alpine-pod.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Pod +metadata: + name: {{.Release.Name}}-{{.Chart.Name}} + labels: + heritage: {{.Release.Service}} + chartName: {{.Chart.Name}} + chartVersion: {{.Chart.Version | quote}} +spec: + restartPolicy: {{default "Never" .restart_policy}} + containers: + - name: waiter + image: "alpine:3.3" + command: ["/bin/sleep","9000"] diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/values.yaml b/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/values.yaml new file mode 100755 index 000000000..6c2aab7ba --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/values.yaml @@ -0,0 +1,2 @@ +# The pod name +name: "my-alpine" diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/charts/mariner-4.3.2.tgz b/pkg/chart/loader/testdata/frobnitz_backslash/charts/mariner-4.3.2.tgz new file mode 100755 index 000000000..3af333e76 Binary files /dev/null and b/pkg/chart/loader/testdata/frobnitz_backslash/charts/mariner-4.3.2.tgz differ diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/docs/README.md b/pkg/chart/loader/testdata/frobnitz_backslash/docs/README.md new file mode 100755 index 000000000..d40747caf --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_backslash/docs/README.md @@ -0,0 +1 @@ +This is a placeholder for documentation. diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/icon.svg b/pkg/chart/loader/testdata/frobnitz_backslash/icon.svg new file mode 100755 index 000000000..892130606 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_backslash/icon.svg @@ -0,0 +1,8 @@ + + + Example icon + + + diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/ignore/me.txt b/pkg/chart/loader/testdata/frobnitz_backslash/ignore/me.txt new file mode 100755 index 000000000..e69de29bb diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/templates/template.tpl b/pkg/chart/loader/testdata/frobnitz_backslash/templates/template.tpl new file mode 100755 index 000000000..c651ee6a0 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_backslash/templates/template.tpl @@ -0,0 +1 @@ +Hello {{.Name | default "world"}} diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/values.yaml b/pkg/chart/loader/testdata/frobnitz_backslash/values.yaml new file mode 100755 index 000000000..61f501258 --- /dev/null +++ b/pkg/chart/loader/testdata/frobnitz_backslash/values.yaml @@ -0,0 +1,6 @@ +# A values file contains configuration. + +name: "Some Name" + +section: + name: "Name in a section" diff --git a/pkg/chart/loader/testdata/genfrob.sh b/pkg/chart/loader/testdata/genfrob.sh new file mode 100755 index 000000000..8f2cddec1 --- /dev/null +++ b/pkg/chart/loader/testdata/genfrob.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +# Pack the albatross chart into the mariner chart. +echo "Packing albatross into mariner" +tar -zcvf mariner/charts/albatross-0.1.0.tgz albatross + +echo "Packing mariner into frobnitz" +tar -zcvf frobnitz/charts/mariner-4.3.2.tgz mariner + +# Pack the frobnitz chart. +echo "Packing frobnitz" +tar --exclude=ignore/* -zcvf frobnitz-1.2.3.tgz frobnitz diff --git a/pkg/chart/loader/testdata/mariner/Chart.yaml b/pkg/chart/loader/testdata/mariner/Chart.yaml new file mode 100644 index 000000000..e2efb7f99 --- /dev/null +++ b/pkg/chart/loader/testdata/mariner/Chart.yaml @@ -0,0 +1,8 @@ +name: mariner +description: A Helm chart for Kubernetes +version: 4.3.2 +home: "" +dependencies: + - name: albatross + repository: https://example.com/mariner/charts + version: "0.1.0" diff --git a/pkg/chart/loader/testdata/mariner/charts/albatross-0.1.0.tgz b/pkg/chart/loader/testdata/mariner/charts/albatross-0.1.0.tgz new file mode 100644 index 000000000..fa8ef5aca Binary files /dev/null and b/pkg/chart/loader/testdata/mariner/charts/albatross-0.1.0.tgz differ diff --git a/pkg/chart/loader/testdata/mariner/templates/placeholder.tpl b/pkg/chart/loader/testdata/mariner/templates/placeholder.tpl new file mode 100644 index 000000000..29c11843a --- /dev/null +++ b/pkg/chart/loader/testdata/mariner/templates/placeholder.tpl @@ -0,0 +1 @@ +# This is a placeholder. diff --git a/pkg/chart/loader/testdata/mariner/values.yaml b/pkg/chart/loader/testdata/mariner/values.yaml new file mode 100644 index 000000000..b0ccb0086 --- /dev/null +++ b/pkg/chart/loader/testdata/mariner/values.yaml @@ -0,0 +1,7 @@ +# Default values for . +# This is a YAML-formatted file. https://github.com/toml-lang/toml +# Declare name/value pairs to be passed into your templates. +# name: "value" + +: + test: true diff --git a/pkg/hapi/chart/metadata.go b/pkg/chart/metadata.go similarity index 95% rename from pkg/hapi/chart/metadata.go rename to pkg/chart/metadata.go index 84bc60096..239bdd2eb 100644 --- a/pkg/hapi/chart/metadata.go +++ b/pkg/chart/metadata.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors All rights reserved. +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 @@ -65,4 +65,6 @@ type Metadata struct { Annotations map[string]string `json:"annotations,omitempty"` // KubeVersion is a SemVer constraint specifying the version of Kubernetes required. KubeVersion string `json:"kubeVersion,omitempty"` + // Requirements are a list of requirements for a chart. + Requirements []*Dependency `json:"dependencies,omitempty"` } diff --git a/pkg/chart/requirements.go b/pkg/chart/requirements.go new file mode 100644 index 000000000..2c17a97c6 --- /dev/null +++ b/pkg/chart/requirements.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 chart + +import "time" + +// Dependency describes a chart upon which another chart depends. +// +// Dependencies can be used to express developer intent, or to capture the state +// of a chart. +type Dependency struct { + // Name is the name of the dependency. + // + // This must mach the name in the dependency's Chart.yaml. + Name string `json:"name"` + // Version is the version (range) of this chart. + // + // A lock file will always produce a single version, while a dependency + // may contain a semantic version range. + Version string `json:"version,omitempty"` + // The URL to the repository. + // + // Appending `index.yaml` to this string should result in a URL that can be + // used to fetch the repository index. + Repository string `json:"repository"` + // A yaml path that resolves to a boolean, used for enabling/disabling charts (e.g. subchart1.enabled ) + Condition string `json:"condition,omitempty"` + // Tags can be used to group charts for enabling/disabling together + Tags []string `json:"tags,omitempty"` + // Enabled bool determines if chart should be loaded + Enabled bool `json:"enabled,omitempty"` + // ImportValues holds the mapping of source values to parent key to be imported. Each item can be a + // string or pair of child/parent sublist items. + ImportValues []interface{} `json:"import-values,omitempty"` + // Alias usable alias to be used for the chart + Alias string `json:"alias,omitempty"` +} + +// Lock is a lock file for requirements. +// +// It represents the state that the dependencies should be in. +type Lock struct { + // Genderated is the date the lock file was last generated. + Generated time.Time `json:"generated"` + // Digest is a hash of the requirements file used to generate it. + Digest string `json:"digest"` + // Dependencies is the list of dependencies that this lock file has locked. + Dependencies []*Dependency `json:"dependencies"` +} diff --git a/pkg/chartutil/capabilities.go b/pkg/chartutil/capabilities.go index 06c117315..55bdcb653 100644 --- a/pkg/chartutil/capabilities.go +++ b/pkg/chartutil/capabilities.go @@ -1,5 +1,5 @@ /* -Copyright 2017 The Kubernetes Authors All rights reserved. +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 @@ -20,13 +20,14 @@ import ( "runtime" "k8s.io/apimachinery/pkg/version" + "k8s.io/client-go/kubernetes/scheme" tversion "k8s.io/helm/pkg/version" ) var ( // DefaultVersionSet is the default version set, which includes only Core V1 ("v1"). - DefaultVersionSet = NewVersionSet("v1") + DefaultVersionSet = allKnownVersions() // DefaultKubeVersion is the default kubernetes version DefaultKubeVersion = &version.Info{ @@ -37,6 +38,12 @@ var ( Compiler: runtime.Compiler, Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH), } + + // DefaultCapabilities is the default set of capabilities. + DefaultCapabilities = &Capabilities{ + APIVersions: DefaultVersionSet, + KubeVersion: DefaultKubeVersion, + } ) // Capabilities describes the capabilities of the Kubernetes cluster that Tiller is attached to. @@ -52,11 +59,11 @@ type Capabilities struct { } // VersionSet is a set of Kubernetes API versions. -type VersionSet map[string]interface{} +type VersionSet map[string]struct{} // NewVersionSet creates a new version set from a list of strings. func NewVersionSet(apiVersions ...string) VersionSet { - vs := VersionSet{} + vs := make(VersionSet) for _, v := range apiVersions { vs[v] = struct{}{} } @@ -70,3 +77,11 @@ func (v VersionSet) Has(apiVersion string) bool { _, ok := v[apiVersion] return ok } + +func allKnownVersions() VersionSet { + vs := make(VersionSet) + for gvk := range scheme.Scheme.AllKnownTypes() { + vs[gvk.GroupVersion().String()] = struct{}{} + } + return vs +} diff --git a/pkg/chartutil/capabilities_test.go b/pkg/chartutil/capabilities_test.go index ac20f0038..8a43214b7 100644 --- a/pkg/chartutil/capabilities_test.go +++ b/pkg/chartutil/capabilities_test.go @@ -1,5 +1,5 @@ /* -Copyright 2017 The Kubernetes Authors All rights reserved. +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 @@ -38,9 +38,6 @@ func TestDefaultVersionSet(t *testing.T) { if !DefaultVersionSet.Has("v1") { t.Error("Expected core v1 version set") } - if d := len(DefaultVersionSet); d != 1 { - t.Errorf("Expected only one version, got %d", d) - } } func TestCapabilities(t *testing.T) { diff --git a/pkg/chartutil/chartfile.go b/pkg/chartutil/chartfile.go index 461e47357..38bcd54db 100644 --- a/pkg/chartutil/chartfile.go +++ b/pkg/chartutil/chartfile.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -24,29 +24,18 @@ import ( "github.com/ghodss/yaml" "github.com/pkg/errors" - "k8s.io/helm/pkg/hapi/chart" + "k8s.io/helm/pkg/chart" ) -// APIVersionv1 is the API version number for version 1. -const APIVersionv1 = "v1" - -// UnmarshalChartfile takes raw Chart.yaml data and unmarshals it. -func UnmarshalChartfile(data []byte) (*chart.Metadata, error) { - y := &chart.Metadata{} - err := yaml.Unmarshal(data, y) - if err != nil { - return nil, err - } - return y, nil -} - // LoadChartfile loads a Chart.yaml file into a *chart.Metadata. func LoadChartfile(filename string) (*chart.Metadata, error) { b, err := ioutil.ReadFile(filename) if err != nil { return nil, err } - return UnmarshalChartfile(b) + y := new(chart.Metadata) + err = yaml.Unmarshal(b, y) + return y, err } // SaveChartfile saves the given metadata as a Chart.yaml file at the given path. @@ -80,8 +69,8 @@ func IsChartDir(dirName string) (bool, error) { return false, errors.Errorf("cannot read Chart.Yaml in directory %q", dirName) } - chartContent, err := UnmarshalChartfile(chartYamlContent) - if err != nil { + chartContent := new(chart.Metadata) + if err := yaml.Unmarshal(chartYamlContent, &chartContent); err != nil { return false, err } if chartContent == nil { diff --git a/pkg/chartutil/chartfile_test.go b/pkg/chartutil/chartfile_test.go index 49de60f65..4c388ef8e 100644 --- a/pkg/chartutil/chartfile_test.go +++ b/pkg/chartutil/chartfile_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -19,7 +19,7 @@ package chartutil import ( "testing" - "k8s.io/helm/pkg/hapi/chart" + "k8s.io/helm/pkg/chart" ) const testfile = "testdata/chartfiletest.yaml" @@ -40,8 +40,8 @@ func verifyChartfile(t *testing.T, f *chart.Metadata, name string) { } // Api instead of API because it was generated via protobuf. - if f.APIVersion != APIVersionv1 { - t.Errorf("Expected API Version %q, got %q", APIVersionv1, f.APIVersion) + if f.APIVersion != chart.APIVersionv1 { + t.Errorf("Expected API Version %q, got %q", chart.APIVersionv1, f.APIVersion) } if f.Name != name { diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go index a2e024047..b5bed235f 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chartutil/create.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -21,10 +21,13 @@ import ( "io/ioutil" "os" "path/filepath" + "strings" + "github.com/ghodss/yaml" "github.com/pkg/errors" - "k8s.io/helm/pkg/hapi/chart" + "k8s.io/helm/pkg/chart" + "k8s.io/helm/pkg/chart/loader" ) const ( @@ -294,7 +297,7 @@ Create chart name and version as used by the chart label. // CreateFrom creates a new chart, but scaffolds it from the src chart. func CreateFrom(chartfile *chart.Metadata, dest, src string) error { - schart, err := Load(src) + schart, err := loader.Load(src) if err != nil { return errors.Wrapf(err, "could not load %s", src) } @@ -304,12 +307,21 @@ func CreateFrom(chartfile *chart.Metadata, dest, src string) error { var updatedTemplates []*chart.File for _, template := range schart.Templates { - newData := Transform(string(template.Data), "", schart.Metadata.Name) + newData := transform(string(template.Data), schart.Name()) updatedTemplates = append(updatedTemplates, &chart.File{Name: template.Name, Data: newData}) } schart.Templates = updatedTemplates - schart.Values = Transform(string(schart.Values), "", schart.Metadata.Name) + b, err := yaml.Marshal(schart.Values) + if err != nil { + return err + } + + var m map[string]interface{} + if err := yaml.Unmarshal(transform(string(b), schart.Name()), &m); err != nil { + return err + } + schart.Values = m return SaveDir(schart, dest) } @@ -378,27 +390,27 @@ func Create(chartfile *chart.Metadata, dir string) (string, error) { { // ingress.yaml path: filepath.Join(cdir, TemplatesDir, IngressFileName), - content: Transform(defaultIngress, "", chartfile.Name), + content: transform(defaultIngress, chartfile.Name), }, { // deployment.yaml path: filepath.Join(cdir, TemplatesDir, DeploymentName), - content: Transform(defaultDeployment, "", chartfile.Name), + content: transform(defaultDeployment, chartfile.Name), }, { // service.yaml path: filepath.Join(cdir, TemplatesDir, ServiceName), - content: Transform(defaultService, "", chartfile.Name), + content: transform(defaultService, chartfile.Name), }, { // NOTES.txt path: filepath.Join(cdir, TemplatesDir, NotesName), - content: Transform(defaultNotes, "", chartfile.Name), + content: transform(defaultNotes, chartfile.Name), }, { // _helpers.tpl path: filepath.Join(cdir, TemplatesDir, HelpersName), - content: Transform(defaultHelpers, "", chartfile.Name), + content: transform(defaultHelpers, chartfile.Name), }, } @@ -413,3 +425,9 @@ func Create(chartfile *chart.Metadata, dir string) (string, error) { } return cdir, nil } + +// transform performs a string replacement of the specified source for +// a given key with the replacement string +func transform(src, replacement string) []byte { + return []byte(strings.Replace(src, "", replacement, -1)) +} diff --git a/pkg/chartutil/create_test.go b/pkg/chartutil/create_test.go index 01f5902a9..79a17b27f 100644 --- a/pkg/chartutil/create_test.go +++ b/pkg/chartutil/create_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -23,7 +23,8 @@ import ( "strings" "testing" - "k8s.io/helm/pkg/hapi/chart" + "k8s.io/helm/pkg/chart" + "k8s.io/helm/pkg/chart/loader" ) func TestCreate(t *testing.T) { @@ -42,13 +43,13 @@ func TestCreate(t *testing.T) { dir := filepath.Join(tdir, "foo") - mychart, err := LoadDir(c) + mychart, err := loader.LoadDir(c) if err != nil { t.Fatalf("Failed to load newly created chart %q: %s", c, err) } - if mychart.Metadata.Name != "foo" { - t.Errorf("Expected name to be 'foo', got %q", mychart.Metadata.Name) + if mychart.Name() != "foo" { + t.Errorf("Expected name to be 'foo', got %q", mychart.Name()) } for _, d := range []string{TemplatesDir, ChartsDir} { @@ -94,13 +95,13 @@ func TestCreateFrom(t *testing.T) { dir := filepath.Join(tdir, "foo") c := filepath.Join(tdir, cf.Name) - mychart, err := LoadDir(c) + mychart, err := loader.LoadDir(c) if err != nil { t.Fatalf("Failed to load newly created chart %q: %s", c, err) } - if mychart.Metadata.Name != "foo" { - t.Errorf("Expected name to be 'foo', got %q", mychart.Metadata.Name) + if mychart.Name() != "foo" { + t.Errorf("Expected name to be 'foo', got %q", mychart.Name()) } for _, d := range []string{TemplatesDir, ChartsDir} { @@ -111,7 +112,7 @@ func TestCreateFrom(t *testing.T) { } } - for _, f := range []string{ChartfileName, ValuesfileName, "requirements.yaml"} { + for _, f := range []string{ChartfileName, ValuesfileName} { if fi, err := os.Stat(filepath.Join(dir, f)); err != nil { t.Errorf("Expected %s file: %s", f, err) } else if fi.IsDir() { @@ -128,7 +129,7 @@ func TestCreateFrom(t *testing.T) { } // Ensure we replace `` - if strings.Contains(string(mychart.Values), "") { - t.Errorf("Did not expect %s to be present in %s", "", string(mychart.Values)) + if strings.Contains(string(mychart.RawValues), "") { + t.Errorf("Did not expect %s to be present in %s", "", string(mychart.RawValues)) } } diff --git a/pkg/chartutil/doc.go b/pkg/chartutil/doc.go index 1190d968d..b479bc075 100644 --- a/pkg/chartutil/doc.go +++ b/pkg/chartutil/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -16,7 +16,7 @@ limitations under the License. /*Package chartutil contains tools for working with charts. -Charts are described in the protocol buffer definition (pkg/proto/hapi/charts). +Charts are described in the protocol buffer definition (pkg/proto/charts). This packe provides utilities for serializing and deserializing charts. A chart can be represented on the file system in one of two ways: @@ -27,18 +27,18 @@ A chart can be represented on the file system in one of two ways: This package provides utilitites for working with those file formats. -The preferred way of loading a chart is using 'chartutil.Load`: +The preferred way of loading a chart is using 'loader.Load`: - chart, err := chartutil.Load(filename) + chart, err := loader.Load(filename) This will attempt to discover whether the file at 'filename' is a directory or a chart archive. It will then load accordingly. For accepting raw compressed tar file data from an io.Reader, the -'chartutil.LoadArchive()' will read in the data, uncompress it, and unpack it +'loader.LoadArchive()' will read in the data, uncompress it, and unpack it into a Chart. -When creating charts in memory, use the 'k8s.io/helm/pkg/proto/hapi/chart' +When creating charts in memory, use the 'k8s.io/helm/pkg/proto/chart' package directly. */ package chartutil // import "k8s.io/helm/pkg/chartutil" diff --git a/pkg/chartutil/expand.go b/pkg/chartutil/expand.go index 126e14e80..983bc17af 100644 --- a/pkg/chartutil/expand.go +++ b/pkg/chartutil/expand.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -40,8 +40,8 @@ func Expand(dir string, r io.Reader) error { return err } - //split header name and create missing directories - d, _ := filepath.Split(header.Name) + // split header name and create missing directories + d := filepath.Dir(header.Name) fullDir := filepath.Join(dir, d) _, err = os.Stat(fullDir) if err != nil && d != "" { @@ -63,8 +63,7 @@ func Expand(dir string, r io.Reader) error { if err != nil { return err } - _, err = io.Copy(file, tr) - if err != nil { + if _, err = io.Copy(file, tr); err != nil { file.Close() return err } diff --git a/pkg/chartutil/files.go b/pkg/chartutil/files.go index ca149a5e7..e04e4f612 100644 --- a/pkg/chartutil/files.go +++ b/pkg/chartutil/files.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 @@ -26,7 +26,7 @@ import ( "github.com/ghodss/yaml" "github.com/gobwas/glob" - "k8s.io/helm/pkg/hapi/chart" + "k8s.io/helm/pkg/chart" ) // Files is a map of files in a chart that can be accessed from a template. @@ -35,7 +35,7 @@ type Files map[string][]byte // NewFiles creates a new Files from chart files. // Given an []*any.Any (the format for files in a chart.Chart), extract a map of files. func NewFiles(from []*chart.File) Files { - files := map[string][]byte{} + files := make(map[string][]byte) for _, f := range from { files[f.Name] = f.Data } @@ -50,11 +50,10 @@ func NewFiles(from []*chart.File) Files { // This is intended to be accessed from within a template, so a missed key returns // an empty []byte. func (f Files) GetBytes(name string) []byte { - v, ok := f[name] - if !ok { - return []byte{} + if v, ok := f[name]; ok { + return v } - return v + return []byte{} } // Get returns a string representation of the given file. @@ -97,7 +96,7 @@ func (f Files) Glob(pattern string) Files { // (regardless of path) should be unique. // // This is designed to be called from a template, and will return empty string -// (via ToYaml function) if it cannot be serialized to YAML, or if the Files +// (via ToYAML function) if it cannot be serialized to YAML, or if the Files // object is nil. // // The output will not be indented, so you will want to pipe this to the @@ -110,14 +109,14 @@ func (f Files) AsConfig() string { return "" } - m := map[string]string{} + m := make(map[string]string) // Explicitly convert to strings, and file names for k, v := range f { m[path.Base(k)] = string(v) } - return ToYaml(m) + return ToYAML(m) } // AsSecrets returns the base64-encoded value of a Files object suitable for @@ -126,7 +125,7 @@ func (f Files) AsConfig() string { // (regardless of path) should be unique. // // This is designed to be called from a template, and will return empty string -// (via ToYaml function) if it cannot be serialized to YAML, or if the Files +// (via ToYAML function) if it cannot be serialized to YAML, or if the Files // object is nil. // // The output will not be indented, so you will want to pipe this to the @@ -139,13 +138,13 @@ func (f Files) AsSecrets() string { return "" } - m := map[string]string{} + m := make(map[string]string) for k, v := range f { m[path.Base(k)] = base64.StdEncoding.EncodeToString(v) } - return ToYaml(m) + return ToYAML(m) } // Lines returns each line of a named file (split by "\n") as a slice, so it can @@ -163,11 +162,11 @@ func (f Files) Lines(path string) []string { return strings.Split(string(f[path]), "\n") } -// ToYaml takes an interface, marshals it to yaml, and returns a string. It will +// ToYAML takes an interface, marshals it to yaml, and returns a string. It will // always return a string, even on marshal error (empty string). // // This is designed to be called from a template. -func ToYaml(v interface{}) string { +func ToYAML(v interface{}) string { data, err := yaml.Marshal(v) if err != nil { // Swallow errors inside of a template. @@ -176,13 +175,13 @@ func ToYaml(v interface{}) string { return strings.TrimSuffix(string(data), "\n") } -// FromYaml converts a YAML document into a map[string]interface{}. +// FromYAML converts a YAML document into a map[string]interface{}. // // This is not a general-purpose YAML parser, and will not parse all valid // YAML documents. Additionally, because its intended use is within templates // it tolerates errors. It will insert the returned error message string into // m["Error"] in the returned map. -func FromYaml(str string) map[string]interface{} { +func FromYAML(str string) map[string]interface{} { m := map[string]interface{}{} if err := yaml.Unmarshal([]byte(str), &m); err != nil { @@ -191,11 +190,11 @@ func FromYaml(str string) map[string]interface{} { return m } -// ToToml takes an interface, marshals it to toml, and returns a string. It will +// ToTOML takes an interface, marshals it to toml, and returns a string. It will // always return a string, even on marshal error (empty string). // // This is designed to be called from a template. -func ToToml(v interface{}) string { +func ToTOML(v interface{}) string { b := bytes.NewBuffer(nil) e := toml.NewEncoder(b) err := e.Encode(v) @@ -205,11 +204,11 @@ func ToToml(v interface{}) string { return b.String() } -// ToJson takes an interface, marshals it to json, and returns a string. It will +// ToJSON takes an interface, marshals it to json, and returns a string. It will // always return a string, even on marshal error (empty string). // // This is designed to be called from a template. -func ToJson(v interface{}) string { +func ToJSON(v interface{}) string { data, err := json.Marshal(v) if err != nil { // Swallow errors inside of a template. @@ -218,14 +217,14 @@ func ToJson(v interface{}) string { return string(data) } -// FromJson converts a JSON document into a map[string]interface{}. +// FromJSON converts a JSON document into a map[string]interface{}. // // This is not a general-purpose JSON parser, and will not parse all valid // JSON documents. Additionally, because its intended use is within templates // it tolerates errors. It will insert the returned error message string into // m["Error"] in the returned map. -func FromJson(str string) map[string]interface{} { - m := map[string]interface{}{} +func FromJSON(str string) map[string]interface{} { + m := make(map[string]interface{}) if err := json.Unmarshal([]byte(str), &m); err != nil { m["Error"] = err.Error() diff --git a/pkg/chartutil/files_test.go b/pkg/chartutil/files_test.go index a6c9d1b65..57fbe88ea 100644 --- a/pkg/chartutil/files_test.go +++ b/pkg/chartutil/files_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 @@ -97,7 +97,7 @@ func TestLines(t *testing.T) { as.Equal("bar", out[0]) } -func TestToYaml(t *testing.T) { +func TestToYAML(t *testing.T) { expect := "foo: bar" v := struct { Foo string `json:"foo"` @@ -105,12 +105,12 @@ func TestToYaml(t *testing.T) { Foo: "bar", } - if got := ToYaml(v); got != expect { + if got := ToYAML(v); got != expect { t.Errorf("Expected %q, got %q", expect, got) } } -func TestToToml(t *testing.T) { +func TestToTOML(t *testing.T) { expect := "foo = \"bar\"\n" v := struct { Foo string `toml:"foo"` @@ -118,7 +118,7 @@ func TestToToml(t *testing.T) { Foo: "bar", } - if got := ToToml(v); got != expect { + if got := ToTOML(v); got != expect { t.Errorf("Expected %q, got %q", expect, got) } @@ -128,19 +128,19 @@ func TestToToml(t *testing.T) { "sail": "white", }, } - got := ToToml(dict) + got := ToTOML(dict) expect = "[mast]\n sail = \"white\"\n" if got != expect { t.Errorf("Expected:\n%s\nGot\n%s\n", expect, got) } } -func TestFromYaml(t *testing.T) { +func TestFromYAML(t *testing.T) { doc := `hello: world one: two: three ` - dict := FromYaml(doc) + dict := FromYAML(doc) if err, ok := dict["Error"]; ok { t.Fatalf("Parse error: %s", err) } @@ -160,13 +160,13 @@ one: - two - three ` - dict = FromYaml(doc2) + dict = FromYAML(doc2) if _, ok := dict["Error"]; !ok { t.Fatal("Expected parser error") } } -func TestToJson(t *testing.T) { +func TestToJSON(t *testing.T) { expect := `{"foo":"bar"}` v := struct { Foo string `json:"foo"` @@ -174,12 +174,12 @@ func TestToJson(t *testing.T) { Foo: "bar", } - if got := ToJson(v); got != expect { + if got := ToJSON(v); got != expect { t.Errorf("Expected %q, got %q", expect, got) } } -func TestFromJson(t *testing.T) { +func TestFromJSON(t *testing.T) { doc := `{ "hello": "world", "one": { @@ -187,7 +187,7 @@ func TestFromJson(t *testing.T) { } } ` - dict := FromJson(doc) + dict := FromJSON(doc) if err, ok := dict["Error"]; ok { t.Fatalf("Parse error: %s", err) } @@ -209,7 +209,7 @@ func TestFromJson(t *testing.T) { "three" ] ` - dict = FromJson(doc2) + dict = FromJSON(doc2) if _, ok := dict["Error"]; !ok { t.Fatal("Expected parser error") } diff --git a/pkg/chartutil/load.go b/pkg/chartutil/load.go deleted file mode 100644 index 44bcbde03..000000000 --- a/pkg/chartutil/load.go +++ /dev/null @@ -1,286 +0,0 @@ -/* -Copyright 2016 The Kubernetes Authors All rights reserved. - -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 chartutil - -import ( - "archive/tar" - "bytes" - "compress/gzip" - "io" - "io/ioutil" - "os" - "path/filepath" - "strings" - - "github.com/pkg/errors" - - "k8s.io/helm/pkg/hapi/chart" - "k8s.io/helm/pkg/ignore" - "k8s.io/helm/pkg/sympath" -) - -// Load takes a string name, tries to resolve it to a file or directory, and then loads it. -// -// This is the preferred way to load a chart. It will discover the chart encoding -// and hand off to the appropriate chart reader. -// -// If a .helmignore file is present, the directory loader will skip loading any files -// matching it. But .helmignore is not evaluated when reading out of an archive. -func Load(name string) (*chart.Chart, error) { - fi, err := os.Stat(name) - if err != nil { - return nil, err - } - if fi.IsDir() { - if validChart, err := IsChartDir(name); !validChart { - return nil, err - } - return LoadDir(name) - } - return LoadFile(name) -} - -// BufferedFile represents an archive file buffered for later processing. -type BufferedFile struct { - Name string - Data []byte -} - -// LoadArchive loads from a reader containing a compressed tar archive. -func LoadArchive(in io.Reader) (*chart.Chart, error) { - unzipped, err := gzip.NewReader(in) - if err != nil { - return &chart.Chart{}, err - } - defer unzipped.Close() - - files := []*BufferedFile{} - tr := tar.NewReader(unzipped) - for { - b := bytes.NewBuffer(nil) - hd, err := tr.Next() - if err == io.EOF { - break - } - if err != nil { - return &chart.Chart{}, err - } - - if hd.FileInfo().IsDir() { - // Use this instead of hd.Typeflag because we don't have to do any - // inference chasing. - continue - } - - // Archive could contain \ if generated on Windows - delimiter := "/" - if strings.ContainsRune(hd.Name, '\\') { - delimiter = "\\" - } - - parts := strings.Split(hd.Name, delimiter) - n := strings.Join(parts[1:], delimiter) - - // Normalize the path to the / delimiter - n = strings.Replace(n, delimiter, "/", -1) - - if parts[0] == "Chart.yaml" { - return nil, errors.New("chart yaml not in base directory") - } - - if _, err := io.Copy(b, tr); err != nil { - return &chart.Chart{}, err - } - - files = append(files, &BufferedFile{Name: n, Data: b.Bytes()}) - b.Reset() - } - - if len(files) == 0 { - return nil, errors.New("no files in chart archive") - } - - return LoadFiles(files) -} - -// LoadFiles loads from in-memory files. -func LoadFiles(files []*BufferedFile) (*chart.Chart, error) { - c := &chart.Chart{} - subcharts := map[string][]*BufferedFile{} - - for _, f := range files { - if f.Name == "Chart.yaml" { - m, err := UnmarshalChartfile(f.Data) - if err != nil { - return c, err - } - c.Metadata = m - } else if f.Name == "values.toml" { - return c, errors.New("values.toml is illegal as of 2.0.0-alpha.2") - } else if f.Name == "values.yaml" { - c.Values = f.Data - } else if strings.HasPrefix(f.Name, "templates/") { - c.Templates = append(c.Templates, &chart.File{Name: f.Name, Data: f.Data}) - } else if strings.HasPrefix(f.Name, "charts/") { - if filepath.Ext(f.Name) == ".prov" { - c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data}) - continue - } - cname := strings.TrimPrefix(f.Name, "charts/") - if strings.IndexAny(cname, "._") == 0 { - // Ignore charts/ that start with . or _. - continue - } - parts := strings.SplitN(cname, "/", 2) - scname := parts[0] - subcharts[scname] = append(subcharts[scname], &BufferedFile{Name: cname, Data: f.Data}) - } else { - c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data}) - } - } - - // Ensure that we got a Chart.yaml file - if c.Metadata == nil { - return c, errors.New("chart metadata (Chart.yaml) missing") - } - if c.Metadata.Name == "" { - return c, errors.New("invalid chart (Chart.yaml): name must not be empty") - } - - for n, files := range subcharts { - var sc *chart.Chart - var err error - if strings.IndexAny(n, "_.") == 0 { - continue - } else if filepath.Ext(n) == ".tgz" { - file := files[0] - if file.Name != n { - return c, errors.Errorf("error unpacking tar in %s: expected %s, got %s", c.Metadata.Name, n, file.Name) - } - // Untar the chart and add to c.Dependencies - b := bytes.NewBuffer(file.Data) - sc, err = LoadArchive(b) - } else { - // We have to trim the prefix off of every file, and ignore any file - // that is in charts/, but isn't actually a chart. - buff := make([]*BufferedFile, 0, len(files)) - for _, f := range files { - parts := strings.SplitN(f.Name, "/", 2) - if len(parts) < 2 { - continue - } - f.Name = parts[1] - buff = append(buff, f) - } - sc, err = LoadFiles(buff) - } - - if err != nil { - return c, errors.Wrapf(err, "error unpacking %s in %s", n, c.Metadata.Name) - } - - c.Dependencies = append(c.Dependencies, sc) - } - - return c, nil -} - -// LoadFile loads from an archive file. -func LoadFile(name string) (*chart.Chart, error) { - if fi, err := os.Stat(name); err != nil { - return nil, err - } else if fi.IsDir() { - return nil, errors.New("cannot load a directory") - } - - raw, err := os.Open(name) - if err != nil { - return nil, err - } - defer raw.Close() - - return LoadArchive(raw) -} - -// LoadDir loads from a directory. -// -// This loads charts only from directories. -func LoadDir(dir string) (*chart.Chart, error) { - topdir, err := filepath.Abs(dir) - if err != nil { - return nil, err - } - - // Just used for errors. - c := &chart.Chart{} - - rules := ignore.Empty() - ifile := filepath.Join(topdir, ignore.HelmIgnore) - if _, err := os.Stat(ifile); err == nil { - r, err := ignore.ParseFile(ifile) - if err != nil { - return c, err - } - rules = r - } - rules.AddDefaults() - - files := []*BufferedFile{} - topdir += string(filepath.Separator) - - walk := func(name string, fi os.FileInfo, err error) error { - n := strings.TrimPrefix(name, topdir) - if n == "" { - // No need to process top level. Avoid bug with helmignore .* matching - // empty names. See issue 1779. - return nil - } - - // Normalize to / since it will also work on Windows - n = filepath.ToSlash(n) - - if err != nil { - return err - } - if fi.IsDir() { - // Directory-based ignore rules should involve skipping the entire - // contents of that directory. - if rules.Ignore(n, fi) { - return filepath.SkipDir - } - return nil - } - - // If a .helmignore file matches, skip this file. - if rules.Ignore(n, fi) { - return nil - } - - data, err := ioutil.ReadFile(name) - if err != nil { - return errors.Wrapf(err, "error reading %s", n) - } - - files = append(files, &BufferedFile{Name: n, Data: data}) - return nil - } - if err = sympath.Walk(topdir, walk); err != nil { - return c, err - } - - return LoadFiles(files) -} diff --git a/pkg/chartutil/requirements.go b/pkg/chartutil/requirements.go index e43c44e13..2325176ed 100644 --- a/pkg/chartutil/requirements.go +++ b/pkg/chartutil/requirements.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 @@ -18,209 +18,88 @@ package chartutil import ( "log" "strings" - "time" "github.com/ghodss/yaml" - "github.com/pkg/errors" - "k8s.io/helm/pkg/hapi/chart" + "k8s.io/helm/pkg/chart" "k8s.io/helm/pkg/version" ) -const ( - requirementsName = "requirements.yaml" - lockfileName = "requirements.lock" -) - -var ( - // ErrRequirementsNotFound indicates that a requirements.yaml is not found. - ErrRequirementsNotFound = errors.New(requirementsName + " not found") - // ErrLockfileNotFound indicates that a requirements.lock is not found. - ErrLockfileNotFound = errors.New(lockfileName + " not found") -) - -// Dependency describes a chart upon which another chart depends. -// -// Dependencies can be used to express developer intent, or to capture the state -// of a chart. -type Dependency struct { - // Name is the name of the dependency. - // - // This must mach the name in the dependency's Chart.yaml. - Name string `json:"name"` - // Version is the version (range) of this chart. - // - // A lock file will always produce a single version, while a dependency - // may contain a semantic version range. - Version string `json:"version,omitempty"` - // The URL to the repository. - // - // Appending `index.yaml` to this string should result in a URL that can be - // used to fetch the repository index. - Repository string `json:"repository"` - // A yaml path that resolves to a boolean, used for enabling/disabling charts (e.g. subchart1.enabled ) - Condition string `json:"condition,omitempty"` - // Tags can be used to group charts for enabling/disabling together - Tags []string `json:"tags,omitempty"` - // Enabled bool determines if chart should be loaded - Enabled bool `json:"enabled,omitempty"` - // ImportValues holds the mapping of source values to parent key to be imported. Each item can be a - // string or pair of child/parent sublist items. - ImportValues []interface{} `json:"import-values,omitempty"` - // Alias usable alias to be used for the chart - Alias string `json:"alias,omitempty"` -} - -// ErrNoRequirementsFile to detect error condition -type ErrNoRequirementsFile error - -// Requirements is a list of requirements for a chart. -// -// Requirements are charts upon which this chart depends. This expresses -// developer intent. -type Requirements struct { - Dependencies []*Dependency `json:"dependencies"` -} - -// RequirementsLock is a lock file for requirements. -// -// It represents the state that the dependencies should be in. -type RequirementsLock struct { - // Genderated is the date the lock file was last generated. - Generated time.Time `json:"generated"` - // Digest is a hash of the requirements file used to generate it. - Digest string `json:"digest"` - // Dependencies is the list of dependencies that this lock file has locked. - Dependencies []*Dependency `json:"dependencies"` -} - -// LoadRequirements loads a requirements file from an in-memory chart. -func LoadRequirements(c *chart.Chart) (*Requirements, error) { - var data []byte - for _, f := range c.Files { - if f.Name == requirementsName { - data = f.Data - } - } - if len(data) == 0 { - return nil, ErrRequirementsNotFound - } - r := &Requirements{} - return r, yaml.Unmarshal(data, r) -} - -// LoadRequirementsLock loads a requirements lock file. -func LoadRequirementsLock(c *chart.Chart) (*RequirementsLock, error) { - var data []byte - for _, f := range c.Files { - if f.Name == lockfileName { - data = f.Data - } - } - if len(data) == 0 { - return nil, ErrLockfileNotFound - } - r := &RequirementsLock{} - return r, yaml.Unmarshal(data, r) -} - // ProcessRequirementsConditions disables charts based on condition path value in values -func ProcessRequirementsConditions(reqs *Requirements, cvals Values) { - var cond string - var conds []string - if reqs == nil || len(reqs.Dependencies) == 0 { +func ProcessRequirementsConditions(reqs []*chart.Dependency, cvals Values) { + if reqs == nil { return } - for _, r := range reqs.Dependencies { + for _, r := range reqs { var hasTrue, hasFalse bool - cond = r.Condition - // check for list - if len(cond) > 0 { - if strings.Contains(cond, ",") { - conds = strings.Split(strings.TrimSpace(cond), ",") - } else { - conds = []string{strings.TrimSpace(cond)} - } - for _, c := range conds { - if len(c) > 0 { - // retrieve value - vv, err := cvals.PathValue(c) - if err == nil { - // if not bool, warn - if bv, ok := vv.(bool); ok { - if bv { - hasTrue = true - } else { - hasFalse = true - } + for _, c := range strings.Split(strings.TrimSpace(r.Condition), ",") { + if len(c) > 0 { + // retrieve value + vv, err := cvals.PathValue(c) + if err == nil { + // if not bool, warn + if bv, ok := vv.(bool); ok { + if bv { + hasTrue = true } else { - log.Printf("Warning: Condition path '%s' for chart %s returned non-bool value", c, r.Name) + hasFalse = true } - } else if _, ok := err.(ErrNoValue); !ok { - // this is a real error - log.Printf("Warning: PathValue returned error %v", err) - - } - if vv != nil { - // got first value, break loop - break + } else { + log.Printf("Warning: Condition path '%s' for chart %s returned non-bool value", c, r.Name) } + } else if _, ok := err.(ErrNoValue); !ok { + // this is a real error + log.Printf("Warning: PathValue returned error %v", err) + } + if vv != nil { + // got first value, break loop + break } - } - if !hasTrue && hasFalse { - r.Enabled = false - } else if hasTrue { - r.Enabled = true - } } + if !hasTrue && hasFalse { + r.Enabled = false + } else if hasTrue { + r.Enabled = true + } } - } // ProcessRequirementsTags disables charts based on tags in values -func ProcessRequirementsTags(reqs *Requirements, cvals Values) { - vt, err := cvals.Table("tags") - if err != nil { +func ProcessRequirementsTags(reqs []*chart.Dependency, cvals Values) { + if reqs == nil { return - } - if reqs == nil || len(reqs.Dependencies) == 0 { + vt, err := cvals.Table("tags") + if err != nil { return } - for _, r := range reqs.Dependencies { - if len(r.Tags) > 0 { - tags := r.Tags - - var hasTrue, hasFalse bool - for _, k := range tags { - if b, ok := vt[k]; ok { - // if not bool, warn - if bv, ok := b.(bool); ok { - if bv { - hasTrue = true - } else { - hasFalse = true - } + for _, r := range reqs { + var hasTrue, hasFalse bool + for _, k := range r.Tags { + if b, ok := vt[k]; ok { + // if not bool, warn + if bv, ok := b.(bool); ok { + if bv { + hasTrue = true } else { - log.Printf("Warning: Tag '%s' for chart %s returned non-bool value", k, r.Name) + hasFalse = true } + } else { + log.Printf("Warning: Tag '%s' for chart %s returned non-bool value", k, r.Name) } } - if !hasTrue && hasFalse { - r.Enabled = false - } else if hasTrue || !hasTrue && !hasFalse { - r.Enabled = true - - } - + } + if !hasTrue && hasFalse { + r.Enabled = false + } else if hasTrue || !hasTrue && !hasFalse { + r.Enabled = true } } - } -func getAliasDependency(charts []*chart.Chart, aliasChart *Dependency) *chart.Chart { +func getAliasDependency(charts []*chart.Chart, aliasChart *chart.Dependency) *chart.Chart { var chartFound chart.Chart for _, existingChart := range charts { if existingChart == nil { @@ -247,27 +126,20 @@ func getAliasDependency(charts []*chart.Chart, aliasChart *Dependency) *chart.Ch } // ProcessRequirementsEnabled removes disabled charts from dependencies -func ProcessRequirementsEnabled(c *chart.Chart, v []byte) error { - reqs, err := LoadRequirements(c) - if err != nil { - // if not just missing requirements file, return error - if nerr, ok := err.(ErrNoRequirementsFile); !ok { - return nerr - } - - // no requirements to process +func ProcessRequirementsEnabled(c *chart.Chart, v map[string]interface{}) error { + if c.Metadata.Requirements == nil { return nil } var chartDependencies []*chart.Chart - // If any dependency is not a part of requirements.yaml + // If any dependency is not a part of Chart.yaml // then this should be added to chartDependencies. - // However, if the dependency is already specified in requirements.yaml - // we should not add it, as it would be anyways processed from requirements.yaml + // However, if the dependency is already specified in Chart.yaml + // we should not add it, as it would be anyways processed from Chart.yaml - for _, existingDependency := range c.Dependencies { + for _, existingDependency := range c.Dependencies() { var dependencyFound bool - for _, req := range reqs.Dependencies { + for _, req := range c.Metadata.Requirements { if existingDependency.Metadata.Name == req.Name && version.IsCompatibleRange(req.Version, existingDependency.Metadata.Version) { dependencyFound = true break @@ -278,58 +150,52 @@ func ProcessRequirementsEnabled(c *chart.Chart, v []byte) error { } } - for _, req := range reqs.Dependencies { - if chartDependency := getAliasDependency(c.Dependencies, req); chartDependency != nil { + for _, req := range c.Metadata.Requirements { + if chartDependency := getAliasDependency(c.Dependencies(), req); chartDependency != nil { chartDependencies = append(chartDependencies, chartDependency) } if req.Alias != "" { req.Name = req.Alias } } - c.Dependencies = chartDependencies + c.SetDependencies(chartDependencies...) // set all to true - for _, lr := range reqs.Dependencies { + for _, lr := range c.Metadata.Requirements { lr.Enabled = true } - cvals, err := CoalesceValues(c, v) - if err != nil { - return err - } - // convert our values back into config - yvals, err := yaml.Marshal(cvals) + b, _ := yaml.Marshal(v) + cvals, err := CoalesceValues(c, b) if err != nil { return err } // flag dependencies as enabled/disabled - ProcessRequirementsTags(reqs, cvals) - ProcessRequirementsConditions(reqs, cvals) + ProcessRequirementsTags(c.Metadata.Requirements, cvals) + ProcessRequirementsConditions(c.Metadata.Requirements, cvals) // make a map of charts to remove - rm := map[string]bool{} - for _, r := range reqs.Dependencies { + rm := map[string]struct{}{} + for _, r := range c.Metadata.Requirements { if !r.Enabled { // remove disabled chart - rm[r.Name] = true + rm[r.Name] = struct{}{} } } // don't keep disabled charts in new slice cd := []*chart.Chart{} - copy(cd, c.Dependencies[:0]) - for _, n := range c.Dependencies { + copy(cd, c.Dependencies()[:0]) + for _, n := range c.Dependencies() { if _, ok := rm[n.Metadata.Name]; !ok { cd = append(cd, n) } - } + // recursively call self to process sub dependencies for _, t := range cd { - err := ProcessRequirementsEnabled(t, yvals) - // if its not just missing requirements file, return error - if nerr, ok := err.(ErrNoRequirementsFile); !ok && err != nil { - return nerr + if err := ProcessRequirementsEnabled(t, cvals); err != nil { + return err } } - c.Dependencies = cd + c.SetDependencies(cd...) return nil } @@ -361,30 +227,13 @@ func pathToMap(path string, data map[string]interface{}) map[string]interface{} n[i][k] = n[z] } } - return n[0] } -// getParents returns a slice of parent charts in reverse order. -func getParents(c *chart.Chart, out []*chart.Chart) []*chart.Chart { - if len(out) == 0 { - out = []*chart.Chart{c} - } - for _, ch := range c.Dependencies { - if len(ch.Dependencies) > 0 { - out = append(out, ch) - out = getParents(ch, out) - } - } - - return out -} - // processImportValues merges values from child to parent based on the chart's dependencies' ImportValues field. func processImportValues(c *chart.Chart) error { - reqs, err := LoadRequirements(c) - if err != nil { - return err + if c.Metadata.Requirements == nil { + return nil } // combine chart values and empty config to get Values cvals, err := CoalesceValues(c, []byte{}) @@ -393,64 +242,56 @@ func processImportValues(c *chart.Chart) error { } b := make(map[string]interface{}) // import values from each dependency if specified in import-values - for _, r := range reqs.Dependencies { - if len(r.ImportValues) > 0 { - var outiv []interface{} - for _, riv := range r.ImportValues { - switch iv := riv.(type) { - case map[string]interface{}: - nm := map[string]string{ - "child": iv["child"].(string), - "parent": iv["parent"].(string), - } - outiv = append(outiv, nm) - s := r.Name + "." + nm["child"] - // get child table - vv, err := cvals.Table(s) - if err != nil { - log.Printf("Warning: ImportValues missing table: %v", err) - continue - } - // create value map from child to be merged into parent - vm := pathToMap(nm["parent"], vv.AsMap()) - b = coalesceTables(cvals, vm) - case string: - nm := map[string]string{ - "child": "exports." + iv, - "parent": ".", - } - outiv = append(outiv, nm) - s := r.Name + "." + nm["child"] - vm, err := cvals.Table(s) - if err != nil { - log.Printf("Warning: ImportValues missing table: %v", err) - continue - } - b = coalesceTables(b, vm.AsMap()) + for _, r := range c.Metadata.Requirements { + var outiv []interface{} + for _, riv := range r.ImportValues { + switch iv := riv.(type) { + case map[string]interface{}: + nm := map[string]string{ + "child": iv["child"].(string), + "parent": iv["parent"].(string), + } + outiv = append(outiv, nm) + // get child table + vv, err := cvals.Table(r.Name + "." + nm["child"]) + if err != nil { + log.Printf("Warning: ImportValues missing table: %v", err) + continue + } + // create value map from child to be merged into parent + vm := pathToMap(nm["parent"], vv.AsMap()) + b = coalesceTables(cvals, vm) + case string: + nm := map[string]string{ + "child": "exports." + iv, + "parent": ".", } + outiv = append(outiv, nm) + vm, err := cvals.Table(r.Name + "." + nm["child"]) + if err != nil { + log.Printf("Warning: ImportValues missing table: %v", err) + continue + } + b = coalesceTables(b, vm.AsMap()) } - // set our formatted import values - r.ImportValues = outiv } - } - b = coalesceTables(b, cvals) - y, err := yaml.Marshal(b) - if err != nil { - return err + // set our formatted import values + r.ImportValues = outiv } // set the new values - c.Values = y + c.Values = coalesceTables(b, cvals) return nil } // ProcessRequirementsImportValues imports specified chart values from child to parent. func ProcessRequirementsImportValues(c *chart.Chart) error { - pc := getParents(c, nil) - for i := len(pc) - 1; i >= 0; i-- { - processImportValues(pc[i]) + for _, d := range c.Dependencies() { + // recurse + if err := ProcessRequirementsImportValues(d); err != nil { + return err + } } - - return nil + return processImportValues(c) } diff --git a/pkg/chartutil/requirements_test.go b/pkg/chartutil/requirements_test.go index 0206f0d2f..1683c01e2 100644 --- a/pkg/chartutil/requirements_test.go +++ b/pkg/chartutil/requirements_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 @@ -20,171 +20,107 @@ import ( "strconv" - "k8s.io/helm/pkg/hapi/chart" + "github.com/ghodss/yaml" + + "k8s.io/helm/pkg/chart" + "k8s.io/helm/pkg/chart/loader" "k8s.io/helm/pkg/version" ) func TestLoadRequirements(t *testing.T) { - c, err := Load("testdata/frobnitz") + c, err := loader.Load("testdata/frobnitz") if err != nil { t.Fatalf("Failed to load testdata: %s", err) } verifyRequirements(t, c) } -func TestLoadRequirementsLock(t *testing.T) { - c, err := Load("testdata/frobnitz") - if err != nil { - t.Fatalf("Failed to load testdata: %s", err) - } - verifyRequirementsLock(t, c) -} -func TestRequirementsTagsNonValue(t *testing.T) { - c, err := Load("testdata/subpop") - if err != nil { - t.Fatalf("Failed to load testdata: %s", err) - } - // tags with no effect - v := []byte("tags:\n nothinguseful: false\n\n") - // expected charts including duplicates in alphanumeric order - e := []string{"parentchart", "subchart1", "subcharta", "subchartb"} - - verifyRequirementsEnabled(t, c, v, e) -} -func TestRequirementsTagsDisabledL1(t *testing.T) { - c, err := Load("testdata/subpop") - if err != nil { - t.Fatalf("Failed to load testdata: %s", err) - } - // tags disabling a group - v := []byte("tags:\n front-end: false\n\n") - // expected charts including duplicates in alphanumeric order - e := []string{"parentchart"} - - verifyRequirementsEnabled(t, c, v, e) -} -func TestRequirementsTagsEnabledL1(t *testing.T) { - c, err := Load("testdata/subpop") - if err != nil { - t.Fatalf("Failed to load testdata: %s", err) - } - // tags disabling a group and enabling a different group - v := []byte("tags:\n front-end: false\n\n back-end: true\n") - // expected charts including duplicates in alphanumeric order - e := []string{"parentchart", "subchart2", "subchartb", "subchartc"} - - verifyRequirementsEnabled(t, c, v, e) -} - -func TestRequirementsTagsDisabledL2(t *testing.T) { - c, err := Load("testdata/subpop") - if err != nil { - t.Fatalf("Failed to load testdata: %s", err) - } - // tags disabling only children, children still enabled since tag front-end=true in values.yaml - v := []byte("tags:\n subcharta: false\n\n subchartb: false\n") - // expected charts including duplicates in alphanumeric order - e := []string{"parentchart", "subchart1", "subcharta", "subchartb"} - - verifyRequirementsEnabled(t, c, v, e) -} -func TestRequirementsTagsDisabledL1Mixed(t *testing.T) { - c, err := Load("testdata/subpop") - if err != nil { - t.Fatalf("Failed to load testdata: %s", err) - } - // tags disabling all parents/children with additional tag re-enabling a parent - v := []byte("tags:\n front-end: false\n\n subchart1: true\n\n back-end: false\n") - // expected charts including duplicates in alphanumeric order - e := []string{"parentchart", "subchart1"} - - verifyRequirementsEnabled(t, c, v, e) -} -func TestRequirementsConditionsNonValue(t *testing.T) { - c, err := Load("testdata/subpop") - if err != nil { - t.Fatalf("Failed to load testdata: %s", err) - } - // tags with no effect - v := []byte("subchart1:\n nothinguseful: false\n\n") - // expected charts including duplicates in alphanumeric order - e := []string{"parentchart", "subchart1", "subcharta", "subchartb"} - - verifyRequirementsEnabled(t, c, v, e) -} -func TestRequirementsConditionsEnabledL1Both(t *testing.T) { - c, err := Load("testdata/subpop") - if err != nil { - t.Fatalf("Failed to load testdata: %s", err) - } - // conditions enabling the parent charts, but back-end (b, c) is still disabled via values.yaml - v := []byte("subchart1:\n enabled: true\nsubchart2:\n enabled: true\n") - // expected charts including duplicates in alphanumeric order - e := []string{"parentchart", "subchart1", "subchart2", "subcharta", "subchartb"} - - verifyRequirementsEnabled(t, c, v, e) -} -func TestRequirementsConditionsDisabledL1Both(t *testing.T) { - c, err := Load("testdata/subpop") +func TestLoadChartLock(t *testing.T) { + c, err := loader.Load("testdata/frobnitz") if err != nil { t.Fatalf("Failed to load testdata: %s", err) } - // conditions disabling the parent charts, effectively disabling children - v := []byte("subchart1:\n enabled: false\nsubchart2:\n enabled: false\n") - // expected charts including duplicates in alphanumeric order - e := []string{"parentchart"} - - verifyRequirementsEnabled(t, c, v, e) + verifyChartLock(t, c) } -func TestRequirementsConditionsSecond(t *testing.T) { - c, err := Load("testdata/subpop") - if err != nil { - t.Fatalf("Failed to load testdata: %s", err) - } - // conditions a child using the second condition path of child's condition - v := []byte("subchart1:\n subcharta:\n enabled: false\n") - // expected charts including duplicates in alphanumeric order - e := []string{"parentchart", "subchart1", "subchartb"} - - verifyRequirementsEnabled(t, c, v, e) -} -func TestRequirementsCombinedDisabledL2(t *testing.T) { - c, err := Load("testdata/subpop") - if err != nil { - t.Fatalf("Failed to load testdata: %s", err) - } - // tags enabling a parent/child group with condition disabling one child - v := []byte("subchartc:\n enabled: false\ntags:\n back-end: true\n") - // expected charts including duplicates in alphanumeric order - e := []string{"parentchart", "subchart1", "subchart2", "subcharta", "subchartb", "subchartb"} - - verifyRequirementsEnabled(t, c, v, e) -} -func TestRequirementsCombinedDisabledL1(t *testing.T) { - c, err := Load("testdata/subpop") - if err != nil { - t.Fatalf("Failed to load testdata: %s", err) +func TestRequirementsEnabled(t *testing.T) { + tests := []struct { + name string + v []byte + e []string // expected charts including duplicates in alphanumeric order + }{{ + "tags with no effect", + []byte("tags:\n nothinguseful: false\n\n"), + []string{"parentchart", "subchart1", "subcharta", "subchartb"}, + }, { + "tags with no effect", + []byte("tags:\n nothinguseful: false\n\n"), + []string{"parentchart", "subchart1", "subcharta", "subchartb"}, + }, { + "tags disabling a group", + []byte("tags:\n front-end: false\n\n"), + []string{"parentchart"}, + }, { + "tags disabling a group and enabling a different group", + []byte("tags:\n front-end: false\n\n back-end: true\n"), + []string{"parentchart", "subchart2", "subchartb", "subchartc"}, + }, { + "tags disabling only children, children still enabled since tag front-end=true in values.yaml", + []byte("tags:\n subcharta: false\n\n subchartb: false\n"), + []string{"parentchart", "subchart1", "subcharta", "subchartb"}, + }, { + "tags disabling all parents/children with additional tag re-enabling a parent", + []byte("tags:\n front-end: false\n\n subchart1: true\n\n back-end: false\n"), + []string{"parentchart", "subchart1"}, + }, { + "tags with no effect", + []byte("subchart1:\n nothinguseful: false\n\n"), + []string{"parentchart", "subchart1", "subcharta", "subchartb"}, + }, { + "conditions enabling the parent charts, but back-end (b, c) is still disabled via values.yaml", + []byte("subchart1:\n enabled: true\nsubchart2:\n enabled: true\n"), + []string{"parentchart", "subchart1", "subchart2", "subcharta", "subchartb"}, + }, { + "conditions disabling the parent charts, effectively disabling children", + []byte("subchart1:\n enabled: false\nsubchart2:\n enabled: false\n"), + []string{"parentchart"}, + }, { + "conditions a child using the second condition path of child's condition", + []byte("subchart1:\n subcharta:\n enabled: false\n"), + []string{"parentchart", "subchart1", "subchartb"}, + }, { + "tags enabling a parent/child group with condition disabling one child", + []byte("subchartc:\n enabled: false\ntags:\n back-end: true\n"), + []string{"parentchart", "subchart1", "subchart2", "subcharta", "subchartb", "subchartb"}, + }, { + "tags will not enable a child if parent is explicitly disabled with condition", + []byte("subchart1:\n enabled: false\ntags:\n front-end: true\n"), + []string{"parentchart"}, + }} + + for _, tc := range tests { + c, err := loader.Load("testdata/subpop") + if err != nil { + t.Fatalf("Failed to load testdata: %s", err) + } + t.Run(tc.name, func(t *testing.T) { + verifyRequirementsEnabled(t, c, tc.v, tc.e) + }) } - // tags will not enable a child if parent is explicitly disabled with condition - v := []byte("subchart1:\n enabled: false\ntags:\n front-end: true\n") - // expected charts including duplicates in alphanumeric order - e := []string{"parentchart"} - - verifyRequirementsEnabled(t, c, v, e) } func verifyRequirementsEnabled(t *testing.T, c *chart.Chart, v []byte, e []string) { - out := []*chart.Chart{} - err := ProcessRequirementsEnabled(c, v) - if err != nil { + var m map[string]interface{} + yaml.Unmarshal(v, &m) + if err := ProcessRequirementsEnabled(c, m); err != nil { t.Errorf("Error processing enabled requirements %v", err) } - out = extractCharts(c, out) + + out := extractCharts(c, nil) // build list of chart names - p := []string{} + var p []string for _, r := range out { - p = append(p, r.Metadata.Name) + p = append(p, r.Name()) } //sort alphanumeric and compare to expectations sort.Strings(p) @@ -201,23 +137,21 @@ func verifyRequirementsEnabled(t *testing.T, c *chart.Chart, v []byte, e []strin // extractCharts recursively searches chart dependencies returning all charts found func extractCharts(c *chart.Chart, out []*chart.Chart) []*chart.Chart { - - if len(c.Metadata.Name) > 0 { + if len(c.Name()) > 0 { out = append(out, c) } - for _, d := range c.Dependencies { + for _, d := range c.Dependencies() { out = extractCharts(d, out) } return out } + func TestProcessRequirementsImportValues(t *testing.T) { - c, err := Load("testdata/subpop") + c, err := loader.Load("testdata/subpop") if err != nil { t.Fatalf("Failed to load testdata: %s", err) } - v := []byte{} - e := make(map[string]string) e["imported-chart1.SC1bool"] = "true" @@ -279,18 +213,14 @@ func TestProcessRequirementsImportValues(t *testing.T) { e["SCBexported2A"] = "blaster" e["global.SC1exported2.all.SC1exported3"] = "SC1expstr" - verifyRequirementsImportValues(t, c, v, e) + verifyRequirementsImportValues(t, c, e) } -func verifyRequirementsImportValues(t *testing.T, c *chart.Chart, v []byte, e map[string]string) { - err := ProcessRequirementsImportValues(c) - if err != nil { - t.Errorf("Error processing import values requirements %v", err) - } - cc, err := ReadValues(c.Values) - if err != nil { - t.Errorf("Error reading import values %v", err) +func verifyRequirementsImportValues(t *testing.T, c *chart.Chart, e map[string]string) { + if err := ProcessRequirementsImportValues(c); err != nil { + t.Fatalf("Error processing import values requirements %v", err) } + cc := Values(c.Values) for kk, vv := range e { pv, err := cc.PathValue(kk) if err != nil { @@ -317,182 +247,203 @@ func verifyRequirementsImportValues(t *testing.T, c *chart.Chart, v []byte, e ma return } } - } } func TestGetAliasDependency(t *testing.T) { - c, err := Load("testdata/frobnitz") + c, err := loader.Load("testdata/frobnitz") if err != nil { t.Fatalf("Failed to load testdata: %s", err) } - req, err := LoadRequirements(c) - if err != nil { - t.Fatalf("Failed to load requirement for testdata: %s", err) - } - if len(req.Dependencies) == 0 { + + req := c.Metadata.Requirements + + if len(req) == 0 { t.Fatalf("There are no requirements to test") } // Success case - aliasChart := getAliasDependency(c.Dependencies, req.Dependencies[0]) + aliasChart := getAliasDependency(c.Dependencies(), req[0]) if aliasChart == nil { - t.Fatalf("Failed to get dependency chart for alias %s", req.Dependencies[0].Name) + t.Fatalf("Failed to get dependency chart for alias %s", req[0].Name) } - if req.Dependencies[0].Alias != "" { - if aliasChart.Metadata.Name != req.Dependencies[0].Alias { - t.Fatalf("Dependency chart name should be %s but got %s", req.Dependencies[0].Alias, aliasChart.Metadata.Name) + if req[0].Alias != "" { + if aliasChart.Name() != req[0].Alias { + t.Fatalf("Dependency chart name should be %s but got %s", req[0].Alias, aliasChart.Name()) } - } else if aliasChart.Metadata.Name != req.Dependencies[0].Name { - t.Fatalf("Dependency chart name should be %s but got %s", req.Dependencies[0].Name, aliasChart.Metadata.Name) + } else if aliasChart.Name() != req[0].Name { + t.Fatalf("Dependency chart name should be %s but got %s", req[0].Name, aliasChart.Name()) } - if req.Dependencies[0].Version != "" { - if !version.IsCompatibleRange(req.Dependencies[0].Version, aliasChart.Metadata.Version) { + if req[0].Version != "" { + if !version.IsCompatibleRange(req[0].Version, aliasChart.Metadata.Version) { t.Fatalf("Dependency chart version is not in the compatible range") } - } // Failure case - req.Dependencies[0].Name = "something-else" - if aliasChart := getAliasDependency(c.Dependencies, req.Dependencies[0]); aliasChart != nil { - t.Fatalf("expected no chart but got %s", aliasChart.Metadata.Name) + req[0].Name = "something-else" + if aliasChart := getAliasDependency(c.Dependencies(), req[0]); aliasChart != nil { + t.Fatalf("expected no chart but got %s", aliasChart.Name()) } - req.Dependencies[0].Version = "something else which is not in the compatible range" - if version.IsCompatibleRange(req.Dependencies[0].Version, aliasChart.Metadata.Version) { + req[0].Version = "something else which is not in the compatible range" + if version.IsCompatibleRange(req[0].Version, aliasChart.Metadata.Version) { t.Fatalf("Dependency chart version which is not in the compatible range should cause a failure other than a success ") } - } func TestDependentChartAliases(t *testing.T) { - c, err := Load("testdata/dependent-chart-alias") + c, err := loader.Load("testdata/dependent-chart-alias") if err != nil { t.Fatalf("Failed to load testdata: %s", err) } - if len(c.Dependencies) == 0 { + if len(c.Dependencies()) == 0 { t.Fatal("There are no dependencies to run this test") } - origLength := len(c.Dependencies) + origLength := len(c.Dependencies()) if err := ProcessRequirementsEnabled(c, c.Values); err != nil { t.Fatalf("Expected no errors but got %q", err) } - if len(c.Dependencies) == origLength { + if len(c.Dependencies()) == origLength { t.Fatal("Expected alias dependencies to be added, but did not got that") } - reqmts, err := LoadRequirements(c) - if err != nil { - t.Fatalf("Cannot load requirements for test chart, %v", err) - } - - if len(c.Dependencies) != len(reqmts.Dependencies) { - t.Fatalf("Expected number of chart dependencies %d, but got %d", len(reqmts.Dependencies), len(c.Dependencies)) + if len(c.Dependencies()) != len(c.Metadata.Requirements) { + t.Fatalf("Expected number of chart dependencies %d, but got %d", len(c.Metadata.Requirements), len(c.Dependencies())) } - } func TestDependentChartWithSubChartsAbsentInRequirements(t *testing.T) { - c, err := Load("testdata/dependent-chart-no-requirements-yaml") + c, err := loader.Load("testdata/dependent-chart-no-requirements-yaml") if err != nil { t.Fatalf("Failed to load testdata: %s", err) } - if len(c.Dependencies) != 2 { - t.Fatalf("Expected 2 dependencies for this chart, but got %d", len(c.Dependencies)) + if len(c.Dependencies()) != 2 { + t.Fatalf("Expected 2 dependencies for this chart, but got %d", len(c.Dependencies())) } - origLength := len(c.Dependencies) + origLength := len(c.Dependencies()) if err := ProcessRequirementsEnabled(c, c.Values); err != nil { t.Fatalf("Expected no errors but got %q", err) } - if len(c.Dependencies) != origLength { + if len(c.Dependencies()) != origLength { t.Fatal("Expected no changes in dependencies to be, but did something got changed") } - } func TestDependentChartWithSubChartsHelmignore(t *testing.T) { - if _, err := Load("testdata/dependent-chart-helmignore"); err != nil { + if _, err := loader.Load("testdata/dependent-chart-helmignore"); err != nil { t.Fatalf("Failed to load testdata: %s", err) } } func TestDependentChartsWithSubChartsSymlink(t *testing.T) { - c, err := Load("testdata/joonix") + c, err := loader.Load("testdata/joonix") if err != nil { t.Fatalf("Failed to load testdata: %s", err) } - if c.Metadata.Name != "joonix" { - t.Fatalf("Unexpected chart name: %s", c.Metadata.Name) + if c.Name() != "joonix" { + t.Fatalf("Unexpected chart name: %s", c.Name()) } - if n := len(c.Dependencies); n != 1 { + if n := len(c.Dependencies()); n != 1 { t.Fatalf("Expected 1 dependency for this chart, but got %d", n) } } func TestDependentChartsWithSubchartsAllSpecifiedInRequirements(t *testing.T) { - c, err := Load("testdata/dependent-chart-with-all-in-requirements-yaml") + c, err := loader.Load("testdata/dependent-chart-with-all-in-requirements-yaml") if err != nil { t.Fatalf("Failed to load testdata: %s", err) } - if len(c.Dependencies) == 0 { + if len(c.Dependencies()) == 0 { t.Fatal("There are no dependencies to run this test") } - origLength := len(c.Dependencies) + origLength := len(c.Dependencies()) if err := ProcessRequirementsEnabled(c, c.Values); err != nil { t.Fatalf("Expected no errors but got %q", err) } - if len(c.Dependencies) != origLength { + if len(c.Dependencies()) != origLength { t.Fatal("Expected no changes in dependencies to be, but did something got changed") } - reqmts, err := LoadRequirements(c) - if err != nil { - t.Fatalf("Cannot load requirements for test chart, %v", err) - } - - if len(c.Dependencies) != len(reqmts.Dependencies) { - t.Fatalf("Expected number of chart dependencies %d, but got %d", len(reqmts.Dependencies), len(c.Dependencies)) + if len(c.Dependencies()) != len(c.Metadata.Requirements) { + t.Fatalf("Expected number of chart dependencies %d, but got %d", len(c.Metadata.Requirements), len(c.Dependencies())) } - } func TestDependentChartsWithSomeSubchartsSpecifiedInRequirements(t *testing.T) { - c, err := Load("testdata/dependent-chart-with-mixed-requirements-yaml") + c, err := loader.Load("testdata/dependent-chart-with-mixed-requirements-yaml") if err != nil { t.Fatalf("Failed to load testdata: %s", err) } - if len(c.Dependencies) == 0 { + if len(c.Dependencies()) == 0 { t.Fatal("There are no dependencies to run this test") } - origLength := len(c.Dependencies) + origLength := len(c.Dependencies()) if err := ProcessRequirementsEnabled(c, c.Values); err != nil { t.Fatalf("Expected no errors but got %q", err) } - if len(c.Dependencies) != origLength { + if len(c.Dependencies()) != origLength { t.Fatal("Expected no changes in dependencies to be, but did something got changed") } - reqmts, err := LoadRequirements(c) - if err != nil { - t.Fatalf("Cannot load requirements for test chart, %v", err) + if len(c.Dependencies()) <= len(c.Metadata.Requirements) { + t.Fatalf("Expected more dependencies than specified in requirements.yaml(%d), but got %d", len(c.Metadata.Requirements), len(c.Dependencies())) } +} - if len(c.Dependencies) <= len(reqmts.Dependencies) { - t.Fatalf("Expected more dependencies than specified in requirements.yaml(%d), but got %d", len(reqmts.Dependencies), len(c.Dependencies)) +func verifyRequirements(t *testing.T, c *chart.Chart) { + if len(c.Metadata.Requirements) != 2 { + t.Errorf("Expected 2 requirements, got %d", len(c.Metadata.Requirements)) + } + tests := []*chart.Dependency{ + {Name: "alpine", Version: "0.1.0", Repository: "https://example.com/charts"}, + {Name: "mariner", Version: "4.3.2", Repository: "https://example.com/charts"}, } + for i, tt := range tests { + d := c.Metadata.Requirements[i] + if d.Name != tt.Name { + t.Errorf("Expected dependency named %q, got %q", tt.Name, d.Name) + } + if d.Version != tt.Version { + t.Errorf("Expected dependency named %q to have version %q, got %q", tt.Name, tt.Version, d.Version) + } + if d.Repository != tt.Repository { + t.Errorf("Expected dependency named %q to have repository %q, got %q", tt.Name, tt.Repository, d.Repository) + } + } +} +func verifyChartLock(t *testing.T, c *chart.Chart) { + if len(c.Metadata.Requirements) != 2 { + t.Errorf("Expected 2 requirements, got %d", len(c.Metadata.Requirements)) + } + tests := []*chart.Dependency{ + {Name: "alpine", Version: "0.1.0", Repository: "https://example.com/charts"}, + {Name: "mariner", Version: "4.3.2", Repository: "https://example.com/charts"}, + } + for i, tt := range tests { + d := c.Metadata.Requirements[i] + if d.Name != tt.Name { + t.Errorf("Expected dependency named %q, got %q", tt.Name, d.Name) + } + if d.Version != tt.Version { + t.Errorf("Expected dependency named %q to have version %q, got %q", tt.Name, tt.Version, d.Version) + } + if d.Repository != tt.Repository { + t.Errorf("Expected dependency named %q to have repository %q, got %q", tt.Name, tt.Repository, d.Repository) + } + } } diff --git a/pkg/chartutil/save.go b/pkg/chartutil/save.go index 610f0aca0..72462f626 100644 --- a/pkg/chartutil/save.go +++ b/pkg/chartutil/save.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -27,7 +27,7 @@ import ( "github.com/ghodss/yaml" "github.com/pkg/errors" - "k8s.io/helm/pkg/hapi/chart" + "k8s.io/helm/pkg/chart" ) var headerBytes = []byte("+aHR0cHM6Ly95b3V0dS5iZS96OVV6MWljandyTQo=") @@ -35,7 +35,7 @@ var headerBytes = []byte("+aHR0cHM6Ly95b3V0dS5iZS96OVV6MWljandyTQo=") // SaveDir saves a chart as files in a directory. func SaveDir(c *chart.Chart, dest string) error { // Create the chart directory - outdir := filepath.Join(dest, c.Metadata.Name) + outdir := filepath.Join(dest, c.Name()) if err := os.Mkdir(outdir, 0755); err != nil { return err } @@ -46,9 +46,10 @@ func SaveDir(c *chart.Chart, dest string) error { } // Save values.yaml - if len(c.Values) > 0 { + if c.Values != nil { vf := filepath.Join(outdir, ValuesfileName) - if err := ioutil.WriteFile(vf, c.Values, 0755); err != nil { + b, _ := yaml.Marshal(c.Values) + if err := ioutil.WriteFile(vf, b, 0755); err != nil { return err } } @@ -83,7 +84,7 @@ func SaveDir(c *chart.Chart, dest string) error { // Save dependencies base := filepath.Join(outdir, ChartsDir) - for _, dep := range c.Dependencies { + for _, dep := range c.Dependencies() { // Here, we write each dependency as a tar file. if _, err := Save(dep, base); err != nil { return err @@ -158,7 +159,7 @@ func Save(c *chart.Chart, outDir string) (string, error) { } func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error { - base := filepath.Join(prefix, c.Metadata.Name) + base := filepath.Join(prefix, c.Name()) // Save Chart.yaml cdata, err := yaml.Marshal(c.Metadata) @@ -170,10 +171,12 @@ func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error { } // Save values.yaml - if len(c.Values) > 0 { - if err := writeToTar(out, base+"/values.yaml", c.Values); err != nil { - return err - } + ydata, err := yaml.Marshal(c.Values) + if err != nil { + return err + } + if err := writeToTar(out, base+"/values.yaml", ydata); err != nil { + return err } // Save templates @@ -193,7 +196,7 @@ func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error { } // Save dependencies - for _, dep := range c.Dependencies { + for _, dep := range c.Dependencies() { if err := writeTarContents(out, dep, base+"/charts"); err != nil { return err } @@ -212,8 +215,6 @@ func writeToTar(out *tar.Writer, name string, body []byte) error { if err := out.WriteHeader(h); err != nil { return err } - if _, err := out.Write(body); err != nil { - return err - } - return nil + _, err := out.Write(body) + return err } diff --git a/pkg/chartutil/save_test.go b/pkg/chartutil/save_test.go index 04db9cf80..e0634081d 100644 --- a/pkg/chartutil/save_test.go +++ b/pkg/chartutil/save_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -17,13 +17,13 @@ limitations under the License. package chartutil import ( - "bytes" "io/ioutil" "os" "strings" "testing" - "k8s.io/helm/pkg/hapi/chart" + "k8s.io/helm/pkg/chart" + "k8s.io/helm/pkg/chart/loader" ) func TestSave(t *testing.T) { @@ -38,7 +38,6 @@ func TestSave(t *testing.T) { Name: "ahab", Version: "1.2.3.4", }, - Values: []byte("ship: Pequod"), Files: []*chart.File{ {Name: "scheherazade/shahryar.txt", Data: []byte("1,001 Nights")}, }, @@ -55,17 +54,18 @@ func TestSave(t *testing.T) { t.Fatalf("Expected %q to end with .tgz", where) } - c2, err := LoadFile(where) + c2, err := loader.LoadFile(where) if err != nil { t.Fatal(err) } - if c2.Metadata.Name != c.Metadata.Name { - t.Fatalf("Expected chart archive to have %q, got %q", c.Metadata.Name, c2.Metadata.Name) - } - if !bytes.Equal(c2.Values, c.Values) { - t.Fatal("Values data did not match") + if c2.Name() != c.Name() { + t.Fatalf("Expected chart archive to have %q, got %q", c.Name(), c2.Name()) } + // FIXME + // if !bytes.Equal(c2.RawValues, c.RawValues) { + // t.Fatal("Values data did not match") + // } if len(c2.Files) != 1 || c2.Files[0].Name != "scheherazade/shahryar.txt" { t.Fatal("Files data did not match") } @@ -83,7 +83,6 @@ func TestSaveDir(t *testing.T) { Name: "ahab", Version: "1.2.3.4", }, - Values: []byte("ship: Pequod"), Files: []*chart.File{ {Name: "scheherazade/shahryar.txt", Data: []byte("1,001 Nights")}, }, @@ -93,17 +92,18 @@ func TestSaveDir(t *testing.T) { t.Fatalf("Failed to save: %s", err) } - c2, err := LoadDir(tmp + "/ahab") + c2, err := loader.LoadDir(tmp + "/ahab") if err != nil { t.Fatal(err) } - if c2.Metadata.Name != c.Metadata.Name { - t.Fatalf("Expected chart archive to have %q, got %q", c.Metadata.Name, c2.Metadata.Name) - } - if !bytes.Equal(c2.Values, c.Values) { - t.Fatal("Values data did not match") + if c2.Name() != c.Name() { + t.Fatalf("Expected chart archive to have %q, got %q", c.Name(), c2.Name()) } + // FIXME + // if !bytes.Equal(c2.RawValues, c.RawValues) { + // t.Fatal("Values data did not match") + // } if len(c2.Files) != 1 || c2.Files[0].Name != "scheherazade/shahryar.txt" { t.Fatal("Files data did not match") } diff --git a/pkg/chartutil/testdata/frobnitz_backslash/requirements.lock b/pkg/chartutil/testdata/dependent-chart-alias/Chart.lock old mode 100755 new mode 100644 similarity index 100% rename from pkg/chartutil/testdata/frobnitz_backslash/requirements.lock rename to pkg/chartutil/testdata/dependent-chart-alias/Chart.lock diff --git a/pkg/chartutil/testdata/dependent-chart-alias/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-alias/Chart.yaml index 7c071c27b..751a3aa67 100644 --- a/pkg/chartutil/testdata/dependent-chart-alias/Chart.yaml +++ b/pkg/chartutil/testdata/dependent-chart-alias/Chart.yaml @@ -15,3 +15,15 @@ sources: - https://example.com/foo/bar home: http://example.com icon: https://example.com/64x64.png +dependencies: + - name: alpine + version: "0.1.0" + repository: https://example.com/charts + - name: mariner + version: "4.3.2" + repository: https://example.com/charts + alias: mariners2 + - name: mariner + version: "4.3.2" + repository: https://example.com/charts + alias: mariners1 diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/Chart.yaml index 7c071c27b..fe7a99681 100644 --- a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/Chart.yaml +++ b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/Chart.yaml @@ -15,3 +15,10 @@ sources: - https://example.com/foo/bar home: http://example.com icon: https://example.com/64x64.png +dependencies: + - name: alpine + version: "0.1.0" + repository: https://example.com/charts + - name: mariner + version: "4.3.2" + repository: https://example.com/charts diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/Chart.yaml index 7c071c27b..7fc39e28d 100644 --- a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/Chart.yaml +++ b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/Chart.yaml @@ -15,3 +15,7 @@ sources: - https://example.com/foo/bar home: http://example.com icon: https://example.com/64x64.png +dependencies: + - name: alpine + version: "0.1.0" + repository: https://example.com/charts diff --git a/pkg/chartutil/testdata/frobnitz-1.2.3.tgz b/pkg/chartutil/testdata/frobnitz-1.2.3.tgz index fb21cd08f..a8ba8ec6f 100644 Binary files a/pkg/chartutil/testdata/frobnitz-1.2.3.tgz and b/pkg/chartutil/testdata/frobnitz-1.2.3.tgz differ diff --git a/pkg/chartutil/testdata/frobnitz/Chart.lock b/pkg/chartutil/testdata/frobnitz/Chart.lock new file mode 100644 index 000000000..6fcc2ed9f --- /dev/null +++ b/pkg/chartutil/testdata/frobnitz/Chart.lock @@ -0,0 +1,8 @@ +dependencies: + - name: alpine + version: "0.1.0" + repository: https://example.com/charts + - name: mariner + version: "4.3.2" + repository: https://example.com/charts +digest: invalid diff --git a/pkg/chartutil/testdata/frobnitz/Chart.yaml b/pkg/chartutil/testdata/frobnitz/Chart.yaml index 134cd1109..fcd4a4a37 100644 --- a/pkg/chartutil/testdata/frobnitz/Chart.yaml +++ b/pkg/chartutil/testdata/frobnitz/Chart.yaml @@ -18,3 +18,10 @@ icon: https://example.com/64x64.png annotations: extrakey: extravalue anotherkey: anothervalue +dependencies: + - name: alpine + version: "0.1.0" + repository: https://example.com/charts + - name: mariner + version: "4.3.2" + repository: https://example.com/charts diff --git a/pkg/chartutil/testdata/frobnitz/charts/mariner-4.3.2.tgz b/pkg/chartutil/testdata/frobnitz/charts/mariner-4.3.2.tgz index 3af333e76..ea6e2a160 100644 Binary files a/pkg/chartutil/testdata/frobnitz/charts/mariner-4.3.2.tgz and b/pkg/chartutil/testdata/frobnitz/charts/mariner-4.3.2.tgz differ diff --git a/pkg/chartutil/testdata/frobnitz_backslash-1.2.3.tgz b/pkg/chartutil/testdata/frobnitz_backslash-1.2.3.tgz index 692dd6aba..bc6be27aa 100644 Binary files a/pkg/chartutil/testdata/frobnitz_backslash-1.2.3.tgz and b/pkg/chartutil/testdata/frobnitz_backslash-1.2.3.tgz differ diff --git a/pkg/chartutil/testdata/frobnitz_backslash/Chart.lock b/pkg/chartutil/testdata/frobnitz_backslash/Chart.lock new file mode 100755 index 000000000..6fcc2ed9f --- /dev/null +++ b/pkg/chartutil/testdata/frobnitz_backslash/Chart.lock @@ -0,0 +1,8 @@ +dependencies: + - name: alpine + version: "0.1.0" + repository: https://example.com/charts + - name: mariner + version: "4.3.2" + repository: https://example.com/charts +digest: invalid diff --git a/pkg/chartutil/testdata/frobnitz_backslash/Chart.yaml b/pkg/chartutil/testdata/frobnitz_backslash/Chart.yaml index 49df2a046..b1dd40a5d 100755 --- a/pkg/chartutil/testdata/frobnitz_backslash/Chart.yaml +++ b/pkg/chartutil/testdata/frobnitz_backslash/Chart.yaml @@ -18,3 +18,10 @@ icon: https://example.com/64x64.png annotations: extrakey: extravalue anotherkey: anothervalue +dependencies: + - name: alpine + version: "0.1.0" + repository: https://example.com/charts + - name: mariner + version: "4.3.2" + repository: https://example.com/charts diff --git a/pkg/chartutil/testdata/mariner/Chart.yaml b/pkg/chartutil/testdata/mariner/Chart.yaml index 4d52794c6..e2efb7f99 100644 --- a/pkg/chartutil/testdata/mariner/Chart.yaml +++ b/pkg/chartutil/testdata/mariner/Chart.yaml @@ -2,3 +2,7 @@ name: mariner description: A Helm chart for Kubernetes version: 4.3.2 home: "" +dependencies: + - name: albatross + repository: https://example.com/mariner/charts + version: "0.1.0" diff --git a/pkg/chartutil/testdata/mariner/charts/albatross-0.1.0.tgz b/pkg/chartutil/testdata/mariner/charts/albatross-0.1.0.tgz index 0b66d27fe..72bb6b36f 100644 Binary files a/pkg/chartutil/testdata/mariner/charts/albatross-0.1.0.tgz and b/pkg/chartutil/testdata/mariner/charts/albatross-0.1.0.tgz differ diff --git a/pkg/chartutil/testdata/subpop/Chart.yaml b/pkg/chartutil/testdata/subpop/Chart.yaml index bbb0941c3..9af6c6b68 100644 --- a/pkg/chartutil/testdata/subpop/Chart.yaml +++ b/pkg/chartutil/testdata/subpop/Chart.yaml @@ -2,3 +2,34 @@ apiVersion: v1 description: A Helm chart for Kubernetes name: parentchart version: 0.1.0 +dependencies: + - name: subchart1 + repository: http://localhost:10191 + version: 0.1.0 + condition: subchart1.enabled + tags: + - front-end + - subchart1 + import-values: + - child: SC1data + parent: imported-chart1 + - child: SC1data + parent: overridden-chart1 + - child: imported-chartA + parent: imported-chartA + - child: imported-chartA-B + parent: imported-chartA-B + - child: overridden-chartA-B + parent: overridden-chartA-B + - child: SCBexported1A + parent: . + - SCBexported2 + - SC1exported1 + + - name: subchart2 + repository: http://localhost:10191 + version: 0.1.0 + condition: subchart2.enabled + tags: + - back-end + - subchart2 diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/Chart.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/Chart.yaml index f58d82818..f028284b0 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/Chart.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/Chart.yaml @@ -2,3 +2,35 @@ apiVersion: v1 description: A Helm chart for Kubernetes name: subchart1 version: 0.1.0 +dependencies: + - name: subcharta + repository: http://localhost:10191 + version: 0.1.0 + condition: subcharta.enabled,subchart1.subcharta.enabled + tags: + - front-end + - subcharta + import-values: + - child: SCAdata + parent: imported-chartA + - child: SCAdata + parent: overridden-chartA + - child: SCAdata + parent: imported-chartA-B + + - name: subchartb + repository: http://localhost:10191 + version: 0.1.0 + condition: subchartb.enabled + import-values: + - child: SCBdata + parent: imported-chartB + - child: SCBdata + parent: imported-chartA-B + - child: exports.SCBexported2 + parent: exports.SCBexported2 + - SCBexported1 + + tags: + - front-end + - subchartb diff --git a/pkg/chartutil/testdata/subpop/charts/subchart2/Chart.yaml b/pkg/chartutil/testdata/subpop/charts/subchart2/Chart.yaml index 2c76b0786..edd40b52f 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart2/Chart.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart2/Chart.yaml @@ -2,3 +2,18 @@ apiVersion: v1 description: A Helm chart for Kubernetes name: subchart2 version: 0.1.0 +dependencies: + - name: subchartb + repository: http://localhost:10191 + version: 0.1.0 + condition: subchartb.enabled,subchart2.subchartb.enabled + tags: + - back-end + - subchartb + - name: subchartc + repository: http://localhost:10191 + version: 0.1.0 + condition: subchartc.enabled + tags: + - back-end + - subchartc diff --git a/pkg/chartutil/values.go b/pkg/chartutil/values.go index ad499b687..d52a7a4d3 100644 --- a/pkg/chartutil/values.go +++ b/pkg/chartutil/values.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -25,7 +25,7 @@ import ( "github.com/ghodss/yaml" "github.com/pkg/errors" - "k8s.io/helm/pkg/hapi/chart" + "k8s.io/helm/pkg/chart" ) // ErrNoTable indicates that a chart does not have a matching table. @@ -59,11 +59,10 @@ func (v Values) YAML() (string, error) { // // An ErrNoTable is returned if the table does not exist. func (v Values) Table(name string) (Values, error) { - names := strings.Split(name, ".") table := v var err error - for _, n := range names { + for _, n := range strings.Split(name, ".") { table, err = tableLookup(table, n) if err != nil { return table, err @@ -110,7 +109,7 @@ func tableLookup(v Values, simple string) (Values, error) { } var e ErrNoTable = errors.Errorf("no table named %q", simple) - return map[string]interface{}{}, e + return Values{}, e } // ReadValues will parse YAML byte data into a Values. @@ -141,59 +140,48 @@ func ReadValuesFile(filename string) (Values, error) { // - A chart has access to all of the variables for it, as well as all of // the values destined for its dependencies. func CoalesceValues(chrt *chart.Chart, vals []byte) (Values, error) { + var err error cvals := Values{} // Parse values if not nil. We merge these at the top level because // the passed-in values are in the same namespace as the parent chart. if vals != nil { - evals, err := ReadValues(vals) - if err != nil { - return cvals, err - } - cvals, err = coalesce(chrt, evals) + cvals, err = ReadValues(vals) if err != nil { return cvals, err } } - var err error - cvals, err = coalesceDeps(chrt, cvals) - return cvals, err + coalesce(chrt, cvals) + + return coalesceDeps(chrt, cvals) } // coalesce coalesces the dest values and the chart values, giving priority to the dest values. // // This is a helper function for CoalesceValues. -func coalesce(ch *chart.Chart, dest map[string]interface{}) (map[string]interface{}, error) { - var err error - dest, err = coalesceValues(ch, dest) - if err != nil { - return dest, err - } +func coalesce(ch *chart.Chart, dest map[string]interface{}) map[string]interface{} { + coalesceValues(ch, dest) coalesceDeps(ch, dest) - return dest, nil + return dest } // coalesceDeps coalesces the dependencies of the given chart. func coalesceDeps(chrt *chart.Chart, dest map[string]interface{}) (map[string]interface{}, error) { - for _, subchart := range chrt.Dependencies { - if c, ok := dest[subchart.Metadata.Name]; !ok { + for _, subchart := range chrt.Dependencies() { + if c, ok := dest[subchart.Name()]; !ok { // If dest doesn't already have the key, create it. - dest[subchart.Metadata.Name] = map[string]interface{}{} + dest[subchart.Name()] = make(map[string]interface{}) } else if !istable(c) { - return dest, errors.Errorf("type mismatch on %s: %t", subchart.Metadata.Name, c) + return dest, errors.Errorf("type mismatch on %s: %t", subchart.Name(), c) } - if dv, ok := dest[subchart.Metadata.Name]; ok { + if dv, ok := dest[subchart.Name()]; ok { dvmap := dv.(map[string]interface{}) // Get globals out of dest and merge them into dvmap. coalesceGlobals(dvmap, dest) - var err error // Now coalesce the rest of the values. - dest[subchart.Metadata.Name], err = coalesce(subchart, dvmap) - if err != nil { - return dest, err - } + dest[subchart.Name()] = coalesce(subchart, dvmap) } } return dest, nil @@ -202,21 +190,21 @@ func coalesceDeps(chrt *chart.Chart, dest map[string]interface{}) (map[string]in // coalesceGlobals copies the globals out of src and merges them into dest. // // For convenience, returns dest. -func coalesceGlobals(dest, src map[string]interface{}) map[string]interface{} { +func coalesceGlobals(dest, src map[string]interface{}) { var dg, sg map[string]interface{} if destglob, ok := dest[GlobalKey]; !ok { - dg = map[string]interface{}{} + dg = make(map[string]interface{}) } else if dg, ok = destglob.(map[string]interface{}); !ok { log.Printf("warning: skipping globals because destination %s is not a table.", GlobalKey) - return dg + return } if srcglob, ok := src[GlobalKey]; !ok { - sg = map[string]interface{}{} + sg = make(map[string]interface{}) } else if sg, ok = srcglob.(map[string]interface{}); !ok { log.Printf("warning: skipping globals because source %s is not a table.", GlobalKey) - return dg + return } // EXPERIMENTAL: In the past, we have disallowed globals to test tables. This @@ -226,19 +214,19 @@ func coalesceGlobals(dest, src map[string]interface{}) map[string]interface{} { for key, val := range sg { if istable(val) { vv := copyMap(val.(map[string]interface{})) - if destv, ok := dg[key]; ok { - if destvmap, ok := destv.(map[string]interface{}); ok { + if destv, ok := dg[key]; !ok { + // Here there is no merge. We're just adding. + dg[key] = vv + } else { + if destvmap, ok := destv.(map[string]interface{}); !ok { + log.Printf("Conflict: cannot merge map onto non-map for %q. Skipping.", key) + } else { // Basically, we reverse order of coalesce here to merge // top-down. coalesceTables(vv, destvmap) dg[key] = vv continue - } else { - log.Printf("Conflict: cannot merge map onto non-map for %q. Skipping.", key) } - } else { - // Here there is no merge. We're just adding. - dg[key] = vv } } else if dv, ok := dg[key]; ok && istable(dv) { // It's not clear if this condition can actually ever trigger. @@ -249,7 +237,6 @@ func coalesceGlobals(dest, src map[string]interface{}) map[string]interface{} { dg[key] = val } dest[GlobalKey] = dg - return dest } func copyMap(src map[string]interface{}) map[string]interface{} { @@ -263,21 +250,8 @@ func copyMap(src map[string]interface{}) map[string]interface{} { // coalesceValues builds up a values map for a particular chart. // // Values in v will override the values in the chart. -func coalesceValues(c *chart.Chart, v map[string]interface{}) (map[string]interface{}, error) { - // If there are no values in the chart, we just return the given values - if len(c.Values) == 0 { - return v, nil - } - - nv, err := ReadValues(c.Values) - if err != nil { - // On error, we return just the overridden values. - // FIXME: We should log this error. It indicates that the YAML data - // did not parse. - return v, errors.Wrapf(err, "error reading default values (%s)", c.Values) - } - - for key, val := range nv { +func coalesceValues(c *chart.Chart, v map[string]interface{}) { + for key, val := range c.Values { if value, ok := v[key]; ok { if value == nil { // When the YAML value is null, we remove the value's key. @@ -300,7 +274,6 @@ func coalesceValues(c *chart.Chart, v map[string]interface{}) (map[string]interf v[key] = val } } - return v, nil } // coalesceTables merges a source map into a destination map. @@ -340,19 +313,8 @@ type ReleaseOptions struct { // ToRenderValues composes the struct from the data coming from the Releases, Charts and Values files // -// WARNING: This function is deprecated for Helm > 2.1.99 Use ToRenderValuesCaps() instead. It will -// remain in the codebase to stay SemVer compliant. -// -// In Helm 3.0, this will be changed to accept Capabilities as a fourth parameter. -func ToRenderValues(chrt *chart.Chart, chrtVals []byte, options ReleaseOptions) (Values, error) { - caps := &Capabilities{APIVersions: DefaultVersionSet} - return ToRenderValuesCaps(chrt, chrtVals, options, caps) -} - -// ToRenderValuesCaps composes the struct from the data coming from the Releases, Charts and Values files -// // This takes both ReleaseOptions and Capabilities to merge into the render values. -func ToRenderValuesCaps(chrt *chart.Chart, chrtVals []byte, options ReleaseOptions, caps *Capabilities) (Values, error) { +func ToRenderValues(chrt *chart.Chart, chrtVals []byte, options ReleaseOptions, caps *Capabilities) (Values, error) { top := map[string]interface{}{ "Release": map[string]interface{}{ diff --git a/pkg/chartutil/values_test.go b/pkg/chartutil/values_test.go index 8fe1b3be5..d3b6bcdbb 100644 --- a/pkg/chartutil/values_test.go +++ b/pkg/chartutil/values_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -25,7 +25,8 @@ import ( kversion "k8s.io/apimachinery/pkg/version" - "k8s.io/helm/pkg/hapi/chart" + "k8s.io/helm/pkg/chart" + "k8s.io/helm/pkg/chart/loader" "k8s.io/helm/pkg/version" ) @@ -72,12 +73,14 @@ water: func TestToRenderValuesCaps(t *testing.T) { - chartValues := ` -name: al Rashid -where: - city: Basrah - title: caliph -` + chartValues := map[string]interface{}{ + "name": "al Rashid", + "where": map[string]interface{}{ + "city": "Basrah", + "title": "caliph", + }, + } + overideValues := ` name: Haroun where: @@ -88,17 +91,14 @@ where: c := &chart.Chart{ Metadata: &chart.Metadata{Name: "test"}, Templates: []*chart.File{}, - Values: []byte(chartValues), - Dependencies: []*chart.Chart{ - { - Metadata: &chart.Metadata{Name: "where"}, - Values: []byte{}, - }, - }, + Values: chartValues, Files: []*chart.File{ {Name: "scheherazade/shahryar.txt", Data: []byte("1,001 Nights")}, }, } + c.AddDependency(&chart.Chart{ + Metadata: &chart.Metadata{Name: "where"}, + }) v := []byte(overideValues) o := ReleaseOptions{ @@ -112,7 +112,7 @@ where: KubeVersion: &kversion.Info{Major: "1"}, } - res, err := ToRenderValuesCaps(c, v, o, caps) + res, err := ToRenderValues(c, v, o, caps) if err != nil { t.Fatal(err) } @@ -259,10 +259,8 @@ func matchValues(t *testing.T, data map[string]interface{}) { func ttpl(tpl string, v map[string]interface{}) (string, error) { var b bytes.Buffer tt := template.Must(template.New("t").Parse(tpl)) - if err := tt.Execute(&b, v); err != nil { - return "", err - } - return b.String(), nil + err := tt.Execute(&b, v) + return b.String(), err } // ref: http://www.yaml.org/spec/1.2/spec.html#id2803362 @@ -293,7 +291,7 @@ pequod: func TestCoalesceValues(t *testing.T) { tchart := "testdata/moby" - c, err := LoadDir(tchart) + c, err := loader.LoadDir(tchart) if err != nil { t.Fatal(err) } diff --git a/pkg/downloader/chart_downloader.go b/pkg/downloader/chart_downloader.go index ce7c397df..cd65290f3 100644 --- a/pkg/downloader/chart_downloader.go +++ b/pkg/downloader/chart_downloader.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/downloader/chart_downloader_test.go b/pkg/downloader/chart_downloader_test.go index 80efa77e8..5967eee70 100644 --- a/pkg/downloader/chart_downloader_test.go +++ b/pkg/downloader/chart_downloader_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/downloader/doc.go b/pkg/downloader/doc.go index fb54936b8..c70b2f695 100644 --- a/pkg/downloader/doc.go +++ b/pkg/downloader/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go index a59e7dc0d..b80fd8d5c 100644 --- a/pkg/downloader/manager.go +++ b/pkg/downloader/manager.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 @@ -30,9 +30,10 @@ import ( "github.com/ghodss/yaml" "github.com/pkg/errors" + "k8s.io/helm/pkg/chart" + "k8s.io/helm/pkg/chart/loader" "k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/getter" - "k8s.io/helm/pkg/hapi/chart" "k8s.io/helm/pkg/helm/helmpath" "k8s.io/helm/pkg/repo" "k8s.io/helm/pkg/resolver" @@ -72,18 +73,14 @@ func (m *Manager) Build() error { // If a lock file is found, run a build from that. Otherwise, just do // an update. - lock, err := chartutil.LoadRequirementsLock(c) - if err != nil { + lock := c.Lock + if lock == nil { return m.Update() } - // A lock must accompany a requirements.yaml file. - req, err := chartutil.LoadRequirements(c) - if err != nil { - return errors.Wrap(err, "requirements.yaml cannot be opened") - } + req := c.Metadata.Requirements if sum, err := resolver.HashReq(req); err != nil || sum != lock.Digest { - return errors.New("requirements.lock is out of sync with requirements.yaml") + return errors.New("Chart.lock is out of sync with Chart.yaml") } // Check that all of the repos we're dependent on actually exist. @@ -108,7 +105,7 @@ func (m *Manager) Build() error { // Update updates a local charts directory. // -// It first reads the requirements.yaml file, and then attempts to +// It first reads the Chart.yaml file, and then attempts to // negotiate versions based on that. It will download the versions // from remote chart repositories unless SkipUpdate is true. func (m *Manager) Update() error { @@ -119,16 +116,13 @@ func (m *Manager) Update() error { // If no requirements file is found, we consider this a successful // completion. - req, err := chartutil.LoadRequirements(c) - if err != nil { - if err == chartutil.ErrRequirementsNotFound { - fmt.Fprintf(m.Out, "No requirements found in %s/charts.\n", m.ChartPath) - return nil - } - return err + req := c.Metadata.Requirements + if req == nil { + return nil } // Hash requirements.yaml + // FIXME should this hash all of Chart.yaml hash, err := resolver.HashReq(req) if err != nil { return err @@ -136,7 +130,7 @@ func (m *Manager) Update() error { // Check that all of the repos we're dependent on actually exist and // the repo index names. - repoNames, err := m.getRepoNames(req.Dependencies) + repoNames, err := m.getRepoNames(req) if err != nil { return err } @@ -149,7 +143,7 @@ func (m *Manager) Update() error { } // Now we need to find out which version of a chart best satisfies the - // requirements the requirements.yaml + // requirements in the Chart.yaml lock, err := m.resolve(req, repoNames, hash) if err != nil { return err @@ -161,8 +155,8 @@ func (m *Manager) Update() error { } // If the lock file hasn't changed, don't write a new one. - oldLock, err := chartutil.LoadRequirementsLock(c) - if err == nil && oldLock.Digest == lock.Digest { + oldLock := c.Lock + if oldLock != nil && oldLock.Digest == lock.Digest { return nil } @@ -176,13 +170,13 @@ func (m *Manager) loadChartDir() (*chart.Chart, error) { } else if !fi.IsDir() { return nil, errors.New("only unpacked charts can be updated") } - return chartutil.LoadDir(m.ChartPath) + return loader.LoadDir(m.ChartPath) } // resolve takes a list of requirements and translates them into an exact version to download. // // This returns a lock file, which has all of the requirements normalized to a specific version. -func (m *Manager) resolve(req *chartutil.Requirements, repoNames map[string]string, hash string) (*chartutil.RequirementsLock, error) { +func (m *Manager) resolve(req []*chart.Dependency, repoNames map[string]string, hash string) (*chart.Lock, error) { res := resolver.New(m.ChartPath, m.HelmHome) return res.Resolve(req, repoNames, hash) } @@ -191,7 +185,7 @@ func (m *Manager) resolve(req *chartutil.Requirements, repoNames map[string]stri // // It will delete versions of the chart that exist on disk and might cause // a conflict. -func (m *Manager) downloadAll(deps []*chartutil.Dependency) error { +func (m *Manager) downloadAll(deps []*chart.Dependency) error { repos, err := m.loadChartRepositories() if err != nil { return err @@ -307,12 +301,12 @@ func (m *Manager) safeDeleteDep(name, dir string) error { return err } for _, fname := range files { - ch, err := chartutil.LoadFile(fname) + ch, err := loader.LoadFile(fname) if err != nil { fmt.Fprintf(m.Out, "Could not verify %s for deletion: %s (Skipping)", fname, err) continue } - if ch.Metadata.Name != name { + if ch.Name() != name { // This is not the file you are looking for. continue } @@ -325,7 +319,7 @@ func (m *Manager) safeDeleteDep(name, dir string) error { } // hasAllRepos ensures that all of the referenced deps are in the local repo cache. -func (m *Manager) hasAllRepos(deps []*chartutil.Dependency) error { +func (m *Manager) hasAllRepos(deps []*chart.Dependency) error { rf, err := repo.LoadRepositoriesFile(m.HelmHome.RepositoryFile()) if err != nil { return err @@ -335,25 +329,22 @@ func (m *Manager) hasAllRepos(deps []*chartutil.Dependency) error { // Verify that all repositories referenced in the deps are actually known // by Helm. missing := []string{} +Loop: for _, dd := range deps { // If repo is from local path, continue if strings.HasPrefix(dd.Repository, "file://") { continue } - found := false if dd.Repository == "" { - found = true - } else { - for _, repo := range repos { - if urlutil.Equal(repo.URL, strings.TrimSuffix(dd.Repository, "/")) { - found = true - } - } + continue } - if !found { - missing = append(missing, dd.Repository) + for _, repo := range repos { + if urlutil.Equal(repo.URL, strings.TrimSuffix(dd.Repository, "/")) { + continue Loop + } } + missing = append(missing, dd.Repository) } if len(missing) > 0 { return errors.Errorf("no repository definition for %s. Please add the missing repos via 'helm repo add'", strings.Join(missing, ", ")) @@ -362,7 +353,7 @@ func (m *Manager) hasAllRepos(deps []*chartutil.Dependency) error { } // getRepoNames returns the repo names of the referenced deps which can be used to fetch the cahced index file. -func (m *Manager) getRepoNames(deps []*chartutil.Dependency) (map[string]string, error) { +func (m *Manager) getRepoNames(deps []*chart.Dependency) (map[string]string, error) { rf, err := repo.LoadRepositoriesFile(m.HelmHome.RepositoryFile()) if err != nil { return nil, err @@ -408,24 +399,22 @@ func (m *Manager) getRepoNames(deps []*chartutil.Dependency) (map[string]string, } } if len(missing) > 0 { - if len(missing) > 0 { - errorMessage := fmt.Sprintf("no repository definition for %s. Please add them via 'helm repo add'", strings.Join(missing, ", ")) - // It is common for people to try to enter "stable" as a repository instead of the actual URL. - // For this case, let's give them a suggestion. - containsNonURL := false - for _, repo := range missing { - if !strings.Contains(repo, "//") && !strings.HasPrefix(repo, "@") && !strings.HasPrefix(repo, "alias:") { - containsNonURL = true - } + errorMessage := fmt.Sprintf("no repository definition for %s. Please add them via 'helm repo add'", strings.Join(missing, ", ")) + // It is common for people to try to enter "stable" as a repository instead of the actual URL. + // For this case, let's give them a suggestion. + containsNonURL := false + for _, repo := range missing { + if !strings.Contains(repo, "//") && !strings.HasPrefix(repo, "@") && !strings.HasPrefix(repo, "alias:") { + containsNonURL = true } - if containsNonURL { - errorMessage += ` + } + if containsNonURL { + errorMessage += ` Note that repositories must be URLs or aliases. For example, to refer to the stable repository, use "https://kubernetes-charts.storage.googleapis.com/" or "@stable" instead of "stable". Don't forget to add the repo, too ('helm repo add').` - } - return nil, errors.New(errorMessage) } + return nil, errors.New(errorMessage) } return reposMap, nil } @@ -596,12 +585,12 @@ func (m *Manager) loadChartRepositories() (map[string]*repo.ChartRepository, err } // writeLock writes a lockfile to disk -func writeLock(chartpath string, lock *chartutil.RequirementsLock) error { +func writeLock(chartpath string, lock *chart.Lock) error { data, err := yaml.Marshal(lock) if err != nil { return err } - dest := filepath.Join(chartpath, "requirements.lock") + dest := filepath.Join(chartpath, "Chart.lock") return ioutil.WriteFile(dest, data, 0644) } @@ -618,7 +607,7 @@ func tarFromLocalDir(chartpath, name, repo, version string) (string, error) { return "", err } - ch, err := chartutil.LoadDir(origPath) + ch, err := loader.LoadDir(origPath) if err != nil { return "", err } diff --git a/pkg/downloader/manager_test.go b/pkg/downloader/manager_test.go index 1ff2a9c17..d87582dfe 100644 --- a/pkg/downloader/manager_test.go +++ b/pkg/downloader/manager_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 @@ -20,7 +20,7 @@ import ( "reflect" "testing" - "k8s.io/helm/pkg/chartutil" + "k8s.io/helm/pkg/chart" "k8s.io/helm/pkg/helm/helmpath" ) @@ -100,48 +100,48 @@ func TestGetRepoNames(t *testing.T) { } tests := []struct { name string - req []*chartutil.Dependency + req []*chart.Dependency expect map[string]string err bool }{ { name: "no repo definition failure", - req: []*chartutil.Dependency{ + req: []*chart.Dependency{ {Name: "oedipus-rex", Repository: "http://example.com/test"}, }, err: true, }, { name: "no repo definition failure -- stable repo", - req: []*chartutil.Dependency{ + req: []*chart.Dependency{ {Name: "oedipus-rex", Repository: "stable"}, }, err: true, }, { name: "no repo definition failure", - req: []*chartutil.Dependency{ + req: []*chart.Dependency{ {Name: "oedipus-rex", Repository: "http://example.com"}, }, expect: map[string]string{"oedipus-rex": "testing"}, }, { name: "repo from local path", - req: []*chartutil.Dependency{ + req: []*chart.Dependency{ {Name: "local-dep", Repository: "file://./testdata/signtest"}, }, expect: map[string]string{"local-dep": "file://./testdata/signtest"}, }, { name: "repo alias (alias:)", - req: []*chartutil.Dependency{ + req: []*chart.Dependency{ {Name: "oedipus-rex", Repository: "alias:testing"}, }, expect: map[string]string{"oedipus-rex": "testing"}, }, { name: "repo alias (@)", - req: []*chartutil.Dependency{ + req: []*chart.Dependency{ {Name: "oedipus-rex", Repository: "@testing"}, }, expect: map[string]string{"oedipus-rex": "testing"}, diff --git a/pkg/downloader/testdata/helmhome/repository/cache/local-index.yaml b/pkg/downloader/testdata/helmhome/repository/cache/local-index.yaml deleted file mode 120000 index ed068e99e..000000000 --- a/pkg/downloader/testdata/helmhome/repository/cache/local-index.yaml +++ /dev/null @@ -1 +0,0 @@ -repository/local/index.yaml \ No newline at end of file diff --git a/pkg/engine/doc.go b/pkg/engine/doc.go index 53c4084b0..63c036605 100644 --- a/pkg/engine/doc.go +++ b/pkg/engine/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index f09a8ec7c..2d533fdff 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -17,7 +17,6 @@ limitations under the License. package engine import ( - "bytes" "path" "sort" "strings" @@ -26,19 +25,19 @@ import ( "github.com/Masterminds/sprig" "github.com/pkg/errors" + "k8s.io/helm/pkg/chart" "k8s.io/helm/pkg/chartutil" - "k8s.io/helm/pkg/hapi/chart" ) // Engine is an implementation of 'cmd/tiller/environment'.Engine that uses Go templates. type Engine struct { // FuncMap contains the template functions that will be passed to each // render call. This may only be modified before the first call to Render. - FuncMap template.FuncMap + funcMap template.FuncMap // If strict is enabled, template rendering will fail if a template references // a value that was not passed in. Strict bool - CurrentTemplates map[string]renderable + currentTemplates map[string]renderable } // New creates a new Go template Engine instance. @@ -49,10 +48,7 @@ type Engine struct { // The FuncMap sets all of the Sprig functions except for those that provide // access to the underlying OS (env, expandenv). func New() *Engine { - f := FuncMap() - return &Engine{ - FuncMap: f, - } + return &Engine{funcMap: FuncMap()} } // FuncMap returns a mapping of all of the functions that Engine has. @@ -76,11 +72,11 @@ func FuncMap() template.FuncMap { // Add some extra functionality extra := template.FuncMap{ - "toToml": chartutil.ToToml, - "toYaml": chartutil.ToYaml, - "fromYaml": chartutil.FromYaml, - "toJson": chartutil.ToJson, - "fromJson": chartutil.FromJson, + "toToml": chartutil.ToTOML, + "toYaml": chartutil.ToYAML, + "fromYaml": chartutil.FromYAML, + "toJson": chartutil.ToJSON, + "fromJson": chartutil.FromJSON, // This is a placeholder for the "include" function, which is // late-bound to a template. By declaring it here, we preserve the @@ -119,8 +115,8 @@ func FuncMap() template.FuncMap { func (e *Engine) Render(chrt *chart.Chart, values chartutil.Values) (map[string]string, error) { // Render the charts tmap := allTemplates(chrt, values) - e.CurrentTemplates = tmap - return e.render(tmap) + e.currentTemplates = tmap + return e.render(chrt, tmap) } // renderable is an object that can be rendered. @@ -138,18 +134,16 @@ type renderable struct { // The resulting FuncMap is only valid for the passed-in template. func (e *Engine) alterFuncMap(t *template.Template) template.FuncMap { // Clone the func map because we are adding context-specific functions. - var funcMap template.FuncMap = map[string]interface{}{} - for k, v := range e.FuncMap { + funcMap := make(template.FuncMap) + for k, v := range e.funcMap { funcMap[k] = v } // Add the 'include' function here so we can close over t. funcMap["include"] = func(name string, data interface{}) (string, error) { - buf := bytes.NewBuffer(nil) - if err := t.ExecuteTemplate(buf, name, data); err != nil { - return "", err - } - return buf.String(), nil + var buf strings.Builder + err := t.ExecuteTemplate(&buf, name, data) + return buf.String(), err } // Add the 'required' function here @@ -177,15 +171,15 @@ func (e *Engine) alterFuncMap(t *template.Template) template.FuncMap { basePath: basePath.(string), } - templates := map[string]renderable{} templateName, err := vals.PathValue("Template.Name") if err != nil { return "", errors.Wrapf(err, "cannot retrieve Template.Name from values inside tpl function: %s", tpl) } + templates := make(map[string]renderable) templates[templateName.(string)] = r - result, err := e.render(templates) + result, err := e.render(nil, templates) if err != nil { return "", errors.Wrapf(err, "error during tpl function execution for %q", tpl) } @@ -196,7 +190,7 @@ func (e *Engine) alterFuncMap(t *template.Template) template.FuncMap { } // render takes a map of templates/values and renders them. -func (e *Engine) render(tpls map[string]renderable) (rendered map[string]string, err error) { +func (e *Engine) render(ch *chart.Chart, tpls map[string]renderable) (rendered map[string]string, err error) { // Basically, what we do here is start with an empty parent template and then // build up a list of templates -- one for each file. Once all of the templates // have been parsed, we loop through again and execute every template. @@ -228,8 +222,7 @@ func (e *Engine) render(tpls map[string]renderable) (rendered map[string]string, for _, fname := range keys { r := tpls[fname] - t = t.New(fname).Funcs(funcMap) - if _, err := t.Parse(r.tpl); err != nil { + if _, err := t.New(fname).Funcs(funcMap).Parse(r.tpl); err != nil { return map[string]string{}, errors.Wrapf(err, "parse error in %q", fname) } files = append(files, fname) @@ -237,17 +230,15 @@ func (e *Engine) render(tpls map[string]renderable) (rendered map[string]string, // Adding the engine's currentTemplates to the template context // so they can be referenced in the tpl function - for fname, r := range e.CurrentTemplates { + for fname, r := range e.currentTemplates { if t.Lookup(fname) == nil { - t = t.New(fname).Funcs(funcMap) - if _, err := t.Parse(r.tpl); err != nil { + if _, err := t.New(fname).Funcs(funcMap).Parse(r.tpl); err != nil { return map[string]string{}, errors.Wrapf(err, "parse error in %q", fname) } } } rendered = make(map[string]string, len(files)) - var buf bytes.Buffer for _, file := range files { // Don't render partials. We don't care out the direct output of partials. // They are only included from other templates. @@ -256,7 +247,8 @@ func (e *Engine) render(tpls map[string]renderable) (rendered map[string]string, } // At render time, add information about the template that is being rendered. vals := tpls[file].vals - vals["Template"] = map[string]interface{}{"Name": file, "BasePath": tpls[file].basePath} + vals["Template"] = chartutil.Values{"Name": file, "BasePath": tpls[file].basePath} + var buf strings.Builder if err := t.ExecuteTemplate(&buf, file, vals); err != nil { return map[string]string{}, errors.Wrapf(err, "render error in %q", file) } @@ -264,8 +256,14 @@ func (e *Engine) render(tpls map[string]renderable) (rendered map[string]string, // Work around the issue where Go will emit "" even if Options(missing=zero) // is set. Since missing=error will never get here, we do not need to handle // the Strict case. - rendered[file] = strings.Replace(buf.String(), "", "", -1) - buf.Reset() + f := &chart.File{ + Name: strings.Replace(file, "/templates", "/manifests", -1), + Data: []byte(strings.Replace(buf.String(), "", "", -1)), + } + rendered[file] = string(f.Data) + if ch != nil { + ch.Files = append(ch.Files, f) + } } return rendered, nil @@ -299,8 +297,8 @@ func (p byPathLen) Less(i, j int) bool { // // As it goes, it also prepares the values in a scope-sensitive manner. func allTemplates(c *chart.Chart, vals chartutil.Values) map[string]renderable { - templates := map[string]renderable{} - recAllTpls(c, templates, vals, true, "") + templates := make(map[string]renderable) + recAllTpls(c, templates, vals) return templates } @@ -308,44 +306,32 @@ func allTemplates(c *chart.Chart, vals chartutil.Values) map[string]renderable { // // As it recurses, it also sets the values to be appropriate for the template // scope. -func recAllTpls(c *chart.Chart, templates map[string]renderable, parentVals chartutil.Values, top bool, parentID string) { +func recAllTpls(c *chart.Chart, templates map[string]renderable, parentVals chartutil.Values) { // This should never evaluate to a nil map. That will cause problems when // values are appended later. - cvals := chartutil.Values{} - if top { - // If this is the top of the rendering tree, assume that parentVals - // is already resolved to the authoritative values. + cvals := make(chartutil.Values) + if c.IsRoot() { cvals = parentVals - } else if c.Metadata != nil && c.Metadata.Name != "" { - // If there is a {{.Values.ThisChart}} in the parent metadata, - // copy that into the {{.Values}} for this template. - newVals := chartutil.Values{} - if vs, err := parentVals.Table("Values"); err == nil { - if tmp, err := vs.Table(c.Metadata.Name); err == nil { - newVals = tmp - } - } - + } else if c.Name() != "" { cvals = map[string]interface{}{ - "Values": newVals, + "Values": make(chartutil.Values), "Release": parentVals["Release"], "Chart": c.Metadata, "Files": chartutil.NewFiles(c.Files), "Capabilities": parentVals["Capabilities"], } + // If there is a {{.Values.ThisChart}} in the parent metadata, + // copy that into the {{.Values}} for this template. + if vs, err := parentVals.Table("Values." + c.Name()); err == nil { + cvals["Values"] = vs + } } - newParentID := c.Metadata.Name - if parentID != "" { - // We artificially reconstruct the chart path to child templates. This - // creates a namespaced filename that can be used to track down the source - // of a particular template declaration. - newParentID = path.Join(parentID, "charts", newParentID) + for _, child := range c.Dependencies() { + recAllTpls(child, templates, cvals) } - for _, child := range c.Dependencies { - recAllTpls(child, templates, cvals, false, newParentID) - } + newParentID := c.ChartFullPath() for _, t := range c.Templates { templates[path.Join(newParentID, t.Name)] = renderable{ tpl: string(t.Data), diff --git a/pkg/engine/engine_test.go b/pkg/engine/engine_test.go index be9aaa448..de73a8b8e 100644 --- a/pkg/engine/engine_test.go +++ b/pkg/engine/engine_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -21,8 +21,8 @@ import ( "sync" "testing" + "k8s.io/helm/pkg/chart" "k8s.io/helm/pkg/chartutil" - "k8s.io/helm/pkg/hapi/chart" ) func TestSortTemplates(t *testing.T) { @@ -62,7 +62,7 @@ func TestEngine(t *testing.T) { // Forbidden because they allow access to the host OS. forbidden := []string{"env", "expandenv"} for _, f := range forbidden { - if _, ok := e.FuncMap[f]; ok { + if _, ok := e.funcMap[f]; ok { t.Errorf("Forbidden function %s exists in FuncMap.", f) } } @@ -97,7 +97,7 @@ func TestRender(t *testing.T) { {Name: "templates/test2", Data: []byte("{{.global.callme | lower }}")}, {Name: "templates/test3", Data: []byte("{{.noValue}}")}, }, - Values: []byte("outer: DEFAULT\ninner: DEFAULT"), + Values: map[string]interface{}{"outer": "DEFAULT", "inner": "DEFAULT"}, } vals := []byte(` @@ -149,7 +149,7 @@ func TestRenderInternals(t *testing.T) { "three": {tpl: `{{template "two" dict "Value" "three"}}`, vals: vals}, } - out, err := e.render(tpls) + out, err := e.render(nil, tpls) if err != nil { t.Fatalf("Failed template rendering: %s", err) } @@ -182,7 +182,7 @@ func TestParallelRenderInternals(t *testing.T) { tt := fmt.Sprintf("expect-%d", i) v := chartutil.Values{"val": tt} tpls := map[string]renderable{fname: {tpl: `{{.val}}`, vals: v}} - out, err := e.render(tpls) + out, err := e.render(nil, tpls) if err != nil { t.Errorf("Failed to render %s: %s", tt, err) } @@ -202,22 +202,23 @@ func TestAllTemplates(t *testing.T) { {Name: "templates/foo", Data: []byte("foo")}, {Name: "templates/bar", Data: []byte("bar")}, }, - Dependencies: []*chart.Chart{ - { - Metadata: &chart.Metadata{Name: "laboratory mice"}, - Templates: []*chart.File{ - {Name: "templates/pinky", Data: []byte("pinky")}, - {Name: "templates/brain", Data: []byte("brain")}, - }, - Dependencies: []*chart.Chart{{ - Metadata: &chart.Metadata{Name: "same thing we do every night"}, - Templates: []*chart.File{ - {Name: "templates/innermost", Data: []byte("innermost")}, - }}, - }, - }, + } + dep1 := &chart.Chart{ + Metadata: &chart.Metadata{Name: "laboratory mice"}, + Templates: []*chart.File{ + {Name: "templates/pinky", Data: []byte("pinky")}, + {Name: "templates/brain", Data: []byte("brain")}, }, } + ch1.AddDependency(dep1) + + dep2 := &chart.Chart{ + Metadata: &chart.Metadata{Name: "same thing we do every night"}, + Templates: []*chart.File{ + {Name: "templates/innermost", Data: []byte("innermost")}, + }, + } + dep1.AddDependency(dep2) var v chartutil.Values tpls := allTemplates(ch1, v) @@ -235,18 +236,15 @@ func TestRenderDependency(t *testing.T) { Templates: []*chart.File{ {Name: "templates/outer", Data: []byte(toptpl)}, }, - Dependencies: []*chart.Chart{ - { - Metadata: &chart.Metadata{Name: "innerchart"}, - Templates: []*chart.File{ - {Name: "templates/inner", Data: []byte(deptpl)}, - }, - }, - }, } + ch.AddDependency(&chart.Chart{ + Metadata: &chart.Metadata{Name: "innerchart"}, + Templates: []*chart.File{ + {Name: "templates/inner", Data: []byte(deptpl)}, + }, + }) out, err := e.Render(ch, map[string]interface{}{}) - if err != nil { t.Fatalf("failed to render chart: %s", err) } @@ -277,7 +275,7 @@ func TestRenderNestedValues(t *testing.T) { {Name: deepestpath, Data: []byte(`And this same {{.Values.what}} that smiles {{.Values.global.when}}`)}, {Name: checkrelease, Data: []byte(`Tomorrow will be {{default "happy" .Release.Name }}`)}, }, - Values: []byte(`what: "milkshake"`), + Values: map[string]interface{}{"what": "milkshake"}, } inner := &chart.Chart{ @@ -285,22 +283,24 @@ func TestRenderNestedValues(t *testing.T) { Templates: []*chart.File{ {Name: innerpath, Data: []byte(`Old {{.Values.who}} is still a-flyin'`)}, }, - Values: []byte(`who: "Robert"`), - Dependencies: []*chart.Chart{deepest}, + Values: map[string]interface{}{"who": "Robert"}, } + inner.AddDependency(deepest) outer := &chart.Chart{ Metadata: &chart.Metadata{Name: "top"}, Templates: []*chart.File{ {Name: outerpath, Data: []byte(`Gather ye {{.Values.what}} while ye may`)}, }, - Values: []byte(` -what: stinkweed -who: me -herrick: - who: time`), - Dependencies: []*chart.Chart{inner}, + Values: map[string]interface{}{ + "what": "stinkweed", + "who": "me", + "herrick": map[string]interface{}{ + "who": "time", + }, + }, } + outer.AddDependency(inner) injValues := []byte(` what: rosebuds @@ -358,8 +358,6 @@ func TestRenderBuiltinValues(t *testing.T) { {Name: "templates/Lavinia", Data: []byte(`{{.Template.Name}}{{.Chart.Name}}{{.Release.Name}}`)}, {Name: "templates/From", Data: []byte(`{{.Files.author | printf "%s"}} {{.Files.Get "book/title.txt"}}`)}, }, - Values: []byte{}, - Dependencies: []*chart.Chart{}, Files: []*chart.File{ {Name: "author", Data: []byte("Virgil")}, {Name: "book/title.txt", Data: []byte("Aeneid")}, @@ -371,9 +369,8 @@ func TestRenderBuiltinValues(t *testing.T) { Templates: []*chart.File{ {Name: "templates/Aeneas", Data: []byte(`{{.Template.Name}}{{.Chart.Name}}{{.Release.Name}}`)}, }, - Values: []byte{}, - Dependencies: []*chart.Chart{inner}, } + outer.AddDependency(inner) inject := chartutil.Values{ "Values": "", @@ -403,15 +400,13 @@ func TestRenderBuiltinValues(t *testing.T) { } -func TestAlterFuncMap(t *testing.T) { +func TestAlterFuncMap_include(t *testing.T) { c := &chart.Chart{ Metadata: &chart.Metadata{Name: "conrad"}, Templates: []*chart.File{ {Name: "templates/quote", Data: []byte(`{{include "conrad/templates/_partial" . | indent 2}} dead.`)}, {Name: "templates/_partial", Data: []byte(`{{.Release.Name}} - he`)}, }, - Values: []byte{}, - Dependencies: []*chart.Chart{}, } v := chartutil.Values{ @@ -431,127 +426,127 @@ func TestAlterFuncMap(t *testing.T) { if got := out["conrad/templates/quote"]; got != expect { t.Errorf("Expected %q, got %q (%v)", expect, got, out) } +} - reqChart := &chart.Chart{ +func TestAlterFuncMap_require(t *testing.T) { + c := &chart.Chart{ Metadata: &chart.Metadata{Name: "conan"}, Templates: []*chart.File{ {Name: "templates/quote", Data: []byte(`All your base are belong to {{ required "A valid 'who' is required" .Values.who }}`)}, {Name: "templates/bases", Data: []byte(`All {{ required "A valid 'bases' is required" .Values.bases }} of them!`)}, }, - Values: []byte{}, - Dependencies: []*chart.Chart{}, } - reqValues := chartutil.Values{ + v := chartutil.Values{ "Values": chartutil.Values{ "who": "us", "bases": 2, }, - "Chart": reqChart.Metadata, + "Chart": c.Metadata, "Release": chartutil.Values{ "Name": "That 90s meme", }, } - outReq, err := New().Render(reqChart, reqValues) + out, err := New().Render(c, v) if err != nil { t.Fatal(err) } expectStr := "All your base are belong to us" - if gotStr := outReq["conan/templates/quote"]; gotStr != expectStr { - t.Errorf("Expected %q, got %q (%v)", expectStr, gotStr, outReq) + if gotStr := out["conan/templates/quote"]; gotStr != expectStr { + t.Errorf("Expected %q, got %q (%v)", expectStr, gotStr, out) } expectNum := "All 2 of them!" - if gotNum := outReq["conan/templates/bases"]; gotNum != expectNum { - t.Errorf("Expected %q, got %q (%v)", expectNum, gotNum, outReq) + if gotNum := out["conan/templates/bases"]; gotNum != expectNum { + t.Errorf("Expected %q, got %q (%v)", expectNum, gotNum, out) } +} - tplChart := &chart.Chart{ +func TestAlterFuncMap_tpl(t *testing.T) { + c := &chart.Chart{ Metadata: &chart.Metadata{Name: "TplFunction"}, Templates: []*chart.File{ {Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ .Values.value}}" .}}`)}, }, - Values: []byte{}, - Dependencies: []*chart.Chart{}, } - tplValues := chartutil.Values{ + v := chartutil.Values{ "Values": chartutil.Values{ "value": "myvalue", }, - "Chart": tplChart.Metadata, + "Chart": c.Metadata, "Release": chartutil.Values{ "Name": "TestRelease", }, } - outTpl, err := New().Render(tplChart, tplValues) + out, err := New().Render(c, v) if err != nil { t.Fatal(err) } - expectTplStr := "Evaluate tpl Value: myvalue" - if gotStrTpl := outTpl["TplFunction/templates/base"]; gotStrTpl != expectTplStr { - t.Errorf("Expected %q, got %q (%v)", expectTplStr, gotStrTpl, outTpl) + expect := "Evaluate tpl Value: myvalue" + if got := out["TplFunction/templates/base"]; got != expect { + t.Errorf("Expected %q, got %q (%v)", expect, got, out) } +} - tplChartWithFunction := &chart.Chart{ +func TestAlterFuncMap_tplfunc(t *testing.T) { + c := &chart.Chart{ Metadata: &chart.Metadata{Name: "TplFunction"}, Templates: []*chart.File{ {Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ .Values.value | quote}}" .}}`)}, }, - Values: []byte{}, - Dependencies: []*chart.Chart{}, } - tplValuesWithFunction := chartutil.Values{ + v := chartutil.Values{ "Values": chartutil.Values{ "value": "myvalue", }, - "Chart": tplChartWithFunction.Metadata, + "Chart": c.Metadata, "Release": chartutil.Values{ "Name": "TestRelease", }, } - outTplWithFunction, err := New().Render(tplChartWithFunction, tplValuesWithFunction) + out, err := New().Render(c, v) if err != nil { t.Fatal(err) } - expectTplStrWithFunction := "Evaluate tpl Value: \"myvalue\"" - if gotStrTplWithFunction := outTplWithFunction["TplFunction/templates/base"]; gotStrTplWithFunction != expectTplStrWithFunction { - t.Errorf("Expected %q, got %q (%v)", expectTplStrWithFunction, gotStrTplWithFunction, outTplWithFunction) + expect := "Evaluate tpl Value: \"myvalue\"" + if got := out["TplFunction/templates/base"]; got != expect { + t.Errorf("Expected %q, got %q (%v)", expect, got, out) } +} - tplChartWithInclude := &chart.Chart{ +func TestAlterFuncMap_tplinclude(t *testing.T) { + c := &chart.Chart{ Metadata: &chart.Metadata{Name: "TplFunction"}, Templates: []*chart.File{ {Name: "templates/base", Data: []byte(`{{ tpl "{{include ` + "`" + `TplFunction/templates/_partial` + "`" + ` . | quote }}" .}}`)}, {Name: "templates/_partial", Data: []byte(`{{.Template.Name}}`)}, }, - Values: []byte{}, - Dependencies: []*chart.Chart{}, } - tplValueWithInclude := chartutil.Values{ + v := chartutil.Values{ "Values": chartutil.Values{ "value": "myvalue", }, - "Chart": tplChartWithInclude.Metadata, + "Chart": c.Metadata, "Release": chartutil.Values{ "Name": "TestRelease", }, } - outTplWithInclude, err := New().Render(tplChartWithInclude, tplValueWithInclude) + out, err := New().Render(c, v) if err != nil { t.Fatal(err) } - expectedTplStrWithInclude := "\"TplFunction/templates/base\"" - if gotStrTplWithInclude := outTplWithInclude["TplFunction/templates/base"]; gotStrTplWithInclude != expectedTplStrWithInclude { - t.Errorf("Expected %q, got %q (%v)", expectedTplStrWithInclude, gotStrTplWithInclude, outTplWithInclude) + expect := "\"TplFunction/templates/base\"" + if got := out["TplFunction/templates/base"]; got != expect { + t.Errorf("Expected %q, got %q (%v)", expect, got, out) } } diff --git a/pkg/getter/doc.go b/pkg/getter/doc.go index fe51e4967..c53ef1ae0 100644 --- a/pkg/getter/doc.go +++ b/pkg/getter/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/getter/getter.go b/pkg/getter/getter.go index 3456ba33a..75fcc9e77 100644 --- a/pkg/getter/getter.go +++ b/pkg/getter/getter.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/getter/getter_test.go b/pkg/getter/getter_test.go index 6d38a0d28..d03c82686 100644 --- a/pkg/getter/getter_test.go +++ b/pkg/getter/getter_test.go @@ -1,5 +1,5 @@ /* -Copyright 2017 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/getter/httpgetter.go b/pkg/getter/httpgetter.go index b66a5c9b8..ebeb81c8a 100644 --- a/pkg/getter/httpgetter.go +++ b/pkg/getter/httpgetter.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/getter/httpgetter_test.go b/pkg/getter/httpgetter_test.go index fe3fde22a..2b8d1a4e9 100644 --- a/pkg/getter/httpgetter_test.go +++ b/pkg/getter/httpgetter_test.go @@ -1,5 +1,5 @@ /* -Copyright 2017 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/getter/plugingetter.go b/pkg/getter/plugingetter.go index 2ff813f06..6f5b6969e 100644 --- a/pkg/getter/plugingetter.go +++ b/pkg/getter/plugingetter.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/getter/plugingetter_test.go b/pkg/getter/plugingetter_test.go index f1fe9bf29..9bfe6144d 100644 --- a/pkg/getter/plugingetter_test.go +++ b/pkg/getter/plugingetter_test.go @@ -1,5 +1,5 @@ /* -Copyright 2017 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/getter/testdata/repository/cache/local-index.yaml b/pkg/getter/testdata/repository/cache/local-index.yaml deleted file mode 120000 index ed068e99e..000000000 --- a/pkg/getter/testdata/repository/cache/local-index.yaml +++ /dev/null @@ -1 +0,0 @@ -repository/local/index.yaml \ No newline at end of file diff --git a/pkg/hapi/chart/chart.go b/pkg/hapi/chart/chart.go deleted file mode 100644 index 711bee61d..000000000 --- a/pkg/hapi/chart/chart.go +++ /dev/null @@ -1,32 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors All rights reserved. -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 chart - -// Chart is a helm package that contains metadata, a default config, zero or more -// optionally parameterizable templates, and zero or more charts (dependencies). -type Chart struct { - // Metadata is the contents of the Chartfile. - Metadata *Metadata `json:"metadata,omitempty"` - // Templates for this chart. - Templates []*File `json:"templates,omitempty"` - // Dependencies are the charts that this chart depends on. - Dependencies []*Chart `json:"dependencies,omitempty"` - // Values are default config for this template. - Values []byte `json:"values,omitempty"` - // Files are miscellaneous files in a chart archive, - // e.g. README, LICENSE, etc. - Files []*File `json:"files,omitempty"` -} diff --git a/pkg/hapi/release/hook.go b/pkg/hapi/release/hook.go index 608995291..f62cf2f2d 100644 --- a/pkg/hapi/release/hook.go +++ b/pkg/hapi/release/hook.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/hapi/release/info.go b/pkg/hapi/release/info.go index 4b9a995d0..15dbb2377 100644 --- a/pkg/hapi/release/info.go +++ b/pkg/hapi/release/info.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/hapi/release/release.go b/pkg/hapi/release/release.go index f8b739468..9477369c5 100644 --- a/pkg/hapi/release/release.go +++ b/pkg/hapi/release/release.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors All rights reserved. +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 @@ -15,7 +15,7 @@ limitations under the License. package release -import "k8s.io/helm/pkg/hapi/chart" +import "k8s.io/helm/pkg/chart" // Release describes a deployment of a chart, together with the chart // and the variables used to deploy that chart. diff --git a/pkg/hapi/release/status.go b/pkg/hapi/release/status.go index cd7d9e9d2..5e2cb6685 100644 --- a/pkg/hapi/release/status.go +++ b/pkg/hapi/release/status.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors All rights reserved. +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 @@ -22,14 +22,14 @@ const ( StatusUnknown ReleaseStatus = "unknown" // StatusDeployed indicates that the release has been pushed to Kubernetes. StatusDeployed ReleaseStatus = "deployed" - // StatusDeleted indicates that a release has been deleted from Kubermetes. - StatusDeleted ReleaseStatus = "deleted" + // StatusUninstalled indicates that a release has been uninstalled from Kubermetes. + StatusUninstalled ReleaseStatus = "uninstalled" // StatusSuperseded indicates that this release object is outdated and a newer one exists. StatusSuperseded ReleaseStatus = "superseded" // StatusFailed indicates that the release was not successfully deployed. StatusFailed ReleaseStatus = "failed" - // StatusDeleting indicates that a delete operation is underway. - StatusDeleting ReleaseStatus = "deleting" + // StatusUninstalling indicates that a uninstall operation is underway. + StatusUninstalling ReleaseStatus = "uninstalling" // StatusPendingInstall indicates that an install operation is underway. StatusPendingInstall ReleaseStatus = "pending-install" // StatusPendingUpgrade indicates that an upgrade operation is underway. diff --git a/pkg/hapi/release/test_run.go b/pkg/hapi/release/test_run.go index 3d2065539..6fb14416a 100644 --- a/pkg/hapi/release/test_run.go +++ b/pkg/hapi/release/test_run.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/hapi/release/test_suite.go b/pkg/hapi/release/test_suite.go index 26d99be26..f50f83763 100644 --- a/pkg/hapi/release/test_suite.go +++ b/pkg/hapi/release/test_suite.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/hapi/tiller.go b/pkg/hapi/tiller.go index ee30f5619..dbd1580b0 100644 --- a/pkg/hapi/tiller.go +++ b/pkg/hapi/tiller.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors All rights reserved. +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 @@ -16,7 +16,7 @@ limitations under the License. package hapi import ( - "k8s.io/helm/pkg/hapi/chart" + "k8s.io/helm/pkg/chart" "k8s.io/helm/pkg/hapi/release" ) diff --git a/pkg/helm/client.go b/pkg/helm/client.go index 7ce4e912d..4c40b0fbf 100644 --- a/pkg/helm/client.go +++ b/pkg/helm/client.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -17,13 +17,15 @@ limitations under the License. package helm // import "k8s.io/helm/pkg/helm" import ( + yaml "gopkg.in/yaml.v2" + + "k8s.io/helm/pkg/chart" + "k8s.io/helm/pkg/chart/loader" "k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/hapi" - "k8s.io/helm/pkg/hapi/chart" "k8s.io/helm/pkg/hapi/release" "k8s.io/helm/pkg/storage" "k8s.io/helm/pkg/tiller" - "k8s.io/helm/pkg/tiller/environment" ) // Client manages client side of the Helm-Tiller protocol. @@ -39,8 +41,7 @@ func NewClient(opts ...Option) *Client { } func (c *Client) init() *Client { - env := environment.New() - c.tiller = tiller.NewReleaseServer(env, c.opts.discovery, c.opts.kubeClient) + c.tiller = tiller.NewReleaseServer(c.opts.discovery, c.opts.kubeClient) c.tiller.Releases = storage.Init(c.opts.driver) return c } @@ -69,7 +70,7 @@ func (c *Client) ListReleases(opts ...ReleaseListOption) ([]*release.Release, er // InstallRelease loads a chart from chstr, installs it, and returns the release response. func (c *Client) InstallRelease(chstr, ns string, opts ...InstallOption) (*release.Release, error) { // load the chart to install - chart, err := chartutil.Load(chstr) + chart, err := loader.Load(chstr) if err != nil { return nil, err } @@ -94,7 +95,9 @@ func (c *Client) InstallReleaseFromChart(chart *chart.Chart, ns string, opts ... if err := reqOpts.runBefore(req); err != nil { return nil, err } - err := chartutil.ProcessRequirementsEnabled(req.Chart, req.Values) + var m map[string]interface{} + yaml.Unmarshal(req.Values, &m) + err := chartutil.ProcessRequirementsEnabled(req.Chart, m) if err != nil { return nil, err } @@ -106,8 +109,8 @@ func (c *Client) InstallReleaseFromChart(chart *chart.Chart, ns string, opts ... return c.tiller.InstallRelease(req) } -// DeleteRelease uninstalls a named release and returns the response. -func (c *Client) DeleteRelease(rlsName string, opts ...DeleteOption) (*hapi.UninstallReleaseResponse, error) { +// UninstallRelease uninstalls a named release and returns the response. +func (c *Client) UninstallRelease(rlsName string, opts ...UninstallOption) (*hapi.UninstallReleaseResponse, error) { // apply the uninstall options reqOpts := c.opts for _, opt := range opts { @@ -136,7 +139,7 @@ func (c *Client) DeleteRelease(rlsName string, opts ...DeleteOption) (*hapi.Unin // UpdateRelease loads a chart from chstr and updates a release to a new/different chart. func (c *Client) UpdateRelease(rlsName, chstr string, opts ...UpdateOption) (*release.Release, error) { // load the chart to update - chart, err := chartutil.Load(chstr) + chart, err := loader.Load(chstr) if err != nil { return nil, err } @@ -164,12 +167,14 @@ func (c *Client) UpdateReleaseFromChart(rlsName string, chart *chart.Chart, opts if err := reqOpts.runBefore(req); err != nil { return nil, err } - err := chartutil.ProcessRequirementsEnabled(req.Chart, req.Values) - if err != nil { + var m map[string]interface{} + if err := yaml.Unmarshal(req.Values, &m); err != nil { return nil, err } - err = chartutil.ProcessRequirementsImportValues(req.Chart) - if err != nil { + if err := chartutil.ProcessRequirementsEnabled(req.Chart, m); err != nil { + return nil, err + } + if err := chartutil.ProcessRequirementsImportValues(req.Chart); err != nil { return nil, err } diff --git a/pkg/helm/environment/environment.go b/pkg/helm/environment/environment.go index 1a7de9ac2..5bc6f70ca 100644 --- a/pkg/helm/environment/environment.go +++ b/pkg/helm/environment/environment.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/helm/environment/environment_test.go b/pkg/helm/environment/environment_test.go index 3f506b099..7c8b57829 100644 --- a/pkg/helm/environment/environment_test.go +++ b/pkg/helm/environment/environment_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/helm/fake.go b/pkg/helm/fake.go index c3409225f..2228cc485 100644 --- a/pkg/helm/fake.go +++ b/pkg/helm/fake.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -23,8 +23,8 @@ import ( "github.com/pkg/errors" + "k8s.io/helm/pkg/chart" "k8s.io/helm/pkg/hapi" - "k8s.io/helm/pkg/hapi/chart" "k8s.io/helm/pkg/hapi/release" ) @@ -77,8 +77,8 @@ func (c *FakeClient) InstallReleaseFromChart(chart *chart.Chart, ns string, opts return release, nil } -// DeleteRelease deletes a release from the FakeClient -func (c *FakeClient) DeleteRelease(rlsName string, opts ...DeleteOption) (*hapi.UninstallReleaseResponse, error) { +// UninstallRelease uninstalls a release from the FakeClient +func (c *FakeClient) UninstallRelease(rlsName string, opts ...UninstallOption) (*hapi.UninstallReleaseResponse, error) { for i, rel := range c.Rels { if rel.Name == rlsName { c.Rels = append(c.Rels[:i], c.Rels[i+1:]...) @@ -187,7 +187,7 @@ type MockReleaseOptions struct { // ReleaseMock creates a mock release object based on options set by MockReleaseOptions. This function should typically not be used outside of testing. func ReleaseMock(opts *MockReleaseOptions) *release.Release { - date := time.Unix(242085845, 0) + date := time.Unix(242085845, 0).UTC() name := opts.Name if name == "" { diff --git a/pkg/helm/fake_test.go b/pkg/helm/fake_test.go index c41142b0e..66a8f01f4 100644 --- a/pkg/helm/fake_test.go +++ b/pkg/helm/fake_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -20,8 +20,8 @@ import ( "reflect" "testing" + "k8s.io/helm/pkg/chart" "k8s.io/helm/pkg/hapi" - "k8s.io/helm/pkg/hapi/chart" "k8s.io/helm/pkg/hapi/release" ) @@ -182,13 +182,13 @@ func TestFakeClient_InstallReleaseFromChart(t *testing.T) { } } -func TestFakeClient_DeleteRelease(t *testing.T) { +func TestFakeClient_UninstallRelease(t *testing.T) { type fields struct { Rels []*release.Release } type args struct { rlsName string - opts []DeleteOption + opts []UninstallOption } tests := []struct { name string @@ -199,7 +199,7 @@ func TestFakeClient_DeleteRelease(t *testing.T) { wantErr bool }{ { - name: "Delete a release that exists.", + name: "Uninstall a release that exists.", fields: fields{ Rels: []*release.Release{ ReleaseMock(&MockReleaseOptions{Name: "angry-dolphin"}), @@ -208,7 +208,7 @@ func TestFakeClient_DeleteRelease(t *testing.T) { }, args: args{ rlsName: "trepid-tapir", - opts: []DeleteOption{}, + opts: []UninstallOption{}, }, relsAfter: []*release.Release{ ReleaseMock(&MockReleaseOptions{Name: "angry-dolphin"}), @@ -219,7 +219,7 @@ func TestFakeClient_DeleteRelease(t *testing.T) { wantErr: false, }, { - name: "Delete a release that does not exist.", + name: "Uninstall a release that does not exist.", fields: fields{ Rels: []*release.Release{ ReleaseMock(&MockReleaseOptions{Name: "angry-dolphin"}), @@ -228,7 +228,7 @@ func TestFakeClient_DeleteRelease(t *testing.T) { }, args: args{ rlsName: "release-that-does-not-exists", - opts: []DeleteOption{}, + opts: []UninstallOption{}, }, relsAfter: []*release.Release{ ReleaseMock(&MockReleaseOptions{Name: "angry-dolphin"}), @@ -238,7 +238,7 @@ func TestFakeClient_DeleteRelease(t *testing.T) { wantErr: true, }, { - name: "Delete when only 1 item exists.", + name: "Uninstall when only 1 item exists.", fields: fields{ Rels: []*release.Release{ ReleaseMock(&MockReleaseOptions{Name: "trepid-tapir"}), @@ -246,7 +246,7 @@ func TestFakeClient_DeleteRelease(t *testing.T) { }, args: args{ rlsName: "trepid-tapir", - opts: []DeleteOption{}, + opts: []UninstallOption{}, }, relsAfter: []*release.Release{}, want: &hapi.UninstallReleaseResponse{ @@ -260,13 +260,13 @@ func TestFakeClient_DeleteRelease(t *testing.T) { c := &FakeClient{ Rels: tt.fields.Rels, } - got, err := c.DeleteRelease(tt.args.rlsName, tt.args.opts...) + got, err := c.UninstallRelease(tt.args.rlsName, tt.args.opts...) if (err != nil) != tt.wantErr { - t.Errorf("FakeClient.DeleteRelease() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("FakeClient.UninstallRelease() error = %v, wantErr %v", err, tt.wantErr) return } if !reflect.DeepEqual(got, tt.want) { - t.Errorf("FakeClient.DeleteRelease() = %v, want %v", got, tt.want) + t.Errorf("FakeClient.UninstallRelease() = %v, want %v", got, tt.want) } if !reflect.DeepEqual(c.Rels, tt.relsAfter) { diff --git a/pkg/helm/helm_test.go b/pkg/helm/helm_test.go index 16fa2fd9c..d35ad05b4 100644 --- a/pkg/helm/helm_test.go +++ b/pkg/helm/helm_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -23,9 +23,9 @@ import ( "github.com/pkg/errors" - "k8s.io/helm/pkg/chartutil" + cpb "k8s.io/helm/pkg/chart" + "k8s.io/helm/pkg/chart/loader" "k8s.io/helm/pkg/hapi" - cpb "k8s.io/helm/pkg/hapi/chart" rls "k8s.io/helm/pkg/hapi/release" ) @@ -45,7 +45,7 @@ func TestListReleases_VerifyOptions(t *testing.T) { var sortOrd = hapi.SortAsc var codes = []rls.ReleaseStatus{ rls.StatusFailed, - rls.StatusDeleted, + rls.StatusUninstalled, rls.StatusDeployed, rls.StatusSuperseded, } @@ -143,27 +143,27 @@ func TestInstallRelease_VerifyOptions(t *testing.T) { assert(t, "", client.opts.instReq.Name) } -// Verify each DeleteOptions is applied to an UninstallReleaseRequest correctly. -func TestDeleteRelease_VerifyOptions(t *testing.T) { +// Verify each UninstallOptions is applied to an UninstallReleaseRequest correctly. +func TestUninstallRelease_VerifyOptions(t *testing.T) { // Options testdata var releaseName = "test" var disableHooks = true var purgeFlag = true - // Expected DeleteReleaseRequest message + // Expected UninstallReleaseRequest message exp := &hapi.UninstallReleaseRequest{ Name: releaseName, Purge: purgeFlag, DisableHooks: disableHooks, } - // Options used in DeleteRelease - ops := []DeleteOption{ - DeletePurge(purgeFlag), - DeleteDisableHooks(disableHooks), + // Options used in UninstallRelease + ops := []UninstallOption{ + UninstallPurge(purgeFlag), + UninstallDisableHooks(disableHooks), } - // BeforeCall option to intercept Helm client DeleteReleaseRequest + // BeforeCall option to intercept Helm client UninstallReleaseRequest b4c := BeforeCall(func(msg interface{}) error { switch act := msg.(type) { case *hapi.UninstallReleaseRequest: @@ -176,7 +176,7 @@ func TestDeleteRelease_VerifyOptions(t *testing.T) { }) client := NewClient(b4c) - if _, err := client.DeleteRelease(releaseName, ops...); err != errSkip { + if _, err := client.UninstallRelease(releaseName, ops...); err != errSkip { t.Fatalf("did not expect error but got (%v)\n``", err) } @@ -349,7 +349,7 @@ func assert(t *testing.T, expect, actual interface{}) { } func loadChart(t *testing.T, name string) *cpb.Chart { - c, err := chartutil.Load(filepath.Join(chartsDir, name)) + c, err := loader.Load(filepath.Join(chartsDir, name)) if err != nil { t.Fatalf("failed to load test chart (%q): %s\n", name, err) } diff --git a/pkg/helm/helmpath/helmhome.go b/pkg/helm/helmpath/helmhome.go index c8b44b9a7..6227a22dd 100644 --- a/pkg/helm/helmpath/helmhome.go +++ b/pkg/helm/helmpath/helmhome.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/helm/helmpath/helmhome_unix_test.go b/pkg/helm/helmpath/helmhome_unix_test.go index e21a56225..ff866bfc0 100644 --- a/pkg/helm/helmpath/helmhome_unix_test.go +++ b/pkg/helm/helmpath/helmhome_unix_test.go @@ -1,4 +1,4 @@ -// Copyright 2016 The Kubernetes Authors All rights reserved. +// 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 diff --git a/pkg/helm/helmpath/helmhome_windows_test.go b/pkg/helm/helmpath/helmhome_windows_test.go index 138095bf0..b962c6298 100644 --- a/pkg/helm/helmpath/helmhome_windows_test.go +++ b/pkg/helm/helmpath/helmhome_windows_test.go @@ -1,4 +1,4 @@ -// Copyright 2016 The Kubernetes Authors All rights reserved. +// 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 diff --git a/pkg/helm/interface.go b/pkg/helm/interface.go index 39c029d1b..a66eefb23 100644 --- a/pkg/helm/interface.go +++ b/pkg/helm/interface.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -17,8 +17,8 @@ limitations under the License. package helm import ( + "k8s.io/helm/pkg/chart" "k8s.io/helm/pkg/hapi" - "k8s.io/helm/pkg/hapi/chart" "k8s.io/helm/pkg/hapi/release" ) @@ -27,7 +27,7 @@ type Interface interface { ListReleases(opts ...ReleaseListOption) ([]*release.Release, error) InstallRelease(chStr, namespace string, opts ...InstallOption) (*release.Release, error) InstallReleaseFromChart(chart *chart.Chart, namespace string, opts ...InstallOption) (*release.Release, error) - DeleteRelease(rlsName string, opts ...DeleteOption) (*hapi.UninstallReleaseResponse, error) + UninstallRelease(rlsName string, opts ...UninstallOption) (*hapi.UninstallReleaseResponse, error) ReleaseStatus(rlsName string, version int) (*hapi.GetReleaseStatusResponse, error) UpdateRelease(rlsName, chStr string, opts ...UpdateOption) (*release.Release, error) UpdateReleaseFromChart(rlsName string, chart *chart.Chart, opts ...UpdateOption) (*release.Release, error) diff --git a/pkg/helm/option.go b/pkg/helm/option.go index e362483f2..e0d1cda6a 100644 --- a/pkg/helm/option.go +++ b/pkg/helm/option.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -37,7 +37,7 @@ type options struct { reuseName bool // if set, performs pod restart during upgrade/rollback recreate bool - // if set, force resource update through delete/recreate if needed + // if set, force resource update through uninstall/recreate if needed force bool // if set, skip running hooks disableHooks bool @@ -170,8 +170,8 @@ func UpgradeTimeout(timeout int64) UpdateOption { } } -// DeleteTimeout specifies the number of seconds before kubernetes calls timeout -func DeleteTimeout(timeout int64) DeleteOption { +// UninstallTimeout specifies the number of seconds before kubernetes calls timeout +func UninstallTimeout(timeout int64) UninstallOption { return func(opts *options) { opts.uninstallReq.Timeout = timeout } @@ -226,22 +226,22 @@ func UpdateValueOverrides(raw []byte) UpdateOption { } } -// DeleteDisableHooks will disable hooks for a deletion operation. -func DeleteDisableHooks(disable bool) DeleteOption { +// UninstallDisableHooks will disable hooks for a deletion operation. +func UninstallDisableHooks(disable bool) UninstallOption { return func(opts *options) { opts.disableHooks = disable } } -// DeleteDryRun will (if true) execute a deletion as a dry run. -func DeleteDryRun(dry bool) DeleteOption { +// UninstallDryRun will (if true) execute a deletion as a dry run. +func UninstallDryRun(dry bool) UninstallOption { return func(opts *options) { opts.dryRun = dry } } -// DeletePurge removes the release from the store and make its name free for later use. -func DeletePurge(purge bool) DeleteOption { +// UninstallPurge removes the release from the store and make its name free for later use. +func UninstallPurge(purge bool) UninstallOption { return func(opts *options) { opts.uninstallReq.Purge = purge } @@ -289,7 +289,7 @@ func RollbackRecreate(recreate bool) RollbackOption { } } -// RollbackForce will (if true) force resource update through delete/recreate if needed +// RollbackForce will (if true) force resource update through uninstall/recreate if needed func RollbackForce(force bool) RollbackOption { return func(opts *options) { opts.force = force @@ -339,16 +339,16 @@ func UpgradeRecreate(recreate bool) UpdateOption { } } -// UpgradeForce will (if true) force resource update through delete/recreate if needed +// UpgradeForce will (if true) force resource update through uninstall/recreate if needed func UpgradeForce(force bool) UpdateOption { return func(opts *options) { opts.force = force } } -// DeleteOption allows setting optional attributes when +// UninstallOption allows setting optional attributes when // performing a UninstallRelease tiller rpc. -type DeleteOption func(*options) +type UninstallOption func(*options) // UpdateOption allows specifying various settings // configurable by the helm client user for overriding diff --git a/pkg/hooks/hooks.go b/pkg/hooks/hooks.go index e116f82d4..fbcbb9076 100644 --- a/pkg/hooks/hooks.go +++ b/pkg/hooks/hooks.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/ignore/doc.go b/pkg/ignore/doc.go index 7281c33a9..85cc91060 100644 --- a/pkg/ignore/doc.go +++ b/pkg/ignore/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/ignore/rules.go b/pkg/ignore/rules.go index a4ac55c47..096e75411 100644 --- a/pkg/ignore/rules.go +++ b/pkg/ignore/rules.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/ignore/rules_test.go b/pkg/ignore/rules_test.go index 17b8bf403..a2f709097 100644 --- a/pkg/ignore/rules_test.go +++ b/pkg/ignore/rules_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/kube/client.go b/pkg/kube/client.go index bf530408e..3fe2aecf3 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -18,6 +18,7 @@ package kube // import "k8s.io/helm/pkg/kube" import ( "bytes" + "context" "encoding/json" "fmt" "io" @@ -43,38 +44,43 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/strategicpatch" "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/tools/clientcmd" + "k8s.io/cli-runtime/pkg/genericclioptions" + "k8s.io/cli-runtime/pkg/genericclioptions/resource" + "k8s.io/client-go/kubernetes" + watchtools "k8s.io/client-go/tools/watch" + "k8s.io/kubernetes/pkg/api/legacyscheme" batchinternal "k8s.io/kubernetes/pkg/apis/batch" - "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/kubectl" + "k8s.io/kubernetes/pkg/kubectl/cmd/get" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/kubectl/resource" - "k8s.io/kubernetes/pkg/kubectl/validation" - "k8s.io/kubernetes/pkg/printers" ) -const ( - // MissingGetHeader is added to Get's output when a resource is not found. - MissingGetHeader = "==> MISSING\nKIND\t\tNAME\n" -) +// MissingGetHeader is added to Get's output when a resource is not found. +const MissingGetHeader = "==> MISSING\nKIND\t\tNAME\n" // ErrNoObjectsVisited indicates that during a visit operation, no matching objects were found. var ErrNoObjectsVisited = goerrors.New("no objects visited") // Client represents a client capable of communicating with the Kubernetes API. type Client struct { - cmdutil.Factory - Log func(string, ...interface{}) + Factory Factory + Log func(string, ...interface{}) } // New creates a new Client. -func New(config clientcmd.ClientConfig) *Client { +func New(getter genericclioptions.RESTClientGetter) *Client { + if getter == nil { + getter = genericclioptions.NewConfigFlags() + } return &Client{ - Factory: cmdutil.NewFactory(config), + Factory: cmdutil.NewFactory(getter), Log: nopLogger, } } +func (c *Client) KubernetesClientSet() (*kubernetes.Clientset, error) { + return c.Factory.KubernetesClientSet() +} + var nopLogger = func(_ string, _ ...interface{}) {} // ResourceActorFunc performs an action on a single resource. @@ -100,27 +106,24 @@ func (c *Client) Create(namespace string, reader io.Reader, timeout int64, shoul } func (c *Client) namespace() string { - if ns, _, err := c.DefaultNamespace(); err == nil { + if ns, _, err := c.Factory.ToRawKubeConfigLoader().Namespace(); err == nil { return ns } return v1.NamespaceDefault } -func (c *Client) newBuilder(namespace string, reader io.Reader) *resource.Result { - return c.NewBuilder(). - Internal(). +// newBuilder returns a new resource builder for structured api objects. +func (c *Client) newBuilder() *resource.Builder { + return c.Factory.NewBuilder(). ContinueOnError(). - Schema(c.validator()). NamespaceParam(c.namespace()). DefaultNamespace(). RequireNamespace(). - Stream(reader, ""). - Flatten(). - Do() + Flatten() } -func (c *Client) validator() validation.Schema { - schema, err := c.Validator(true) +func (c *Client) validator() resource.ContentValidator { + schema, err := c.Factory.Validator(true) if err != nil { c.Log("warning: failed to load schema: %s", err) } @@ -131,14 +134,9 @@ func (c *Client) validator() validation.Schema { func (c *Client) BuildUnstructured(namespace string, reader io.Reader) (Result, error) { var result Result - result, err := c.NewBuilder(). + result, err := c.newBuilder(). Unstructured(). - ContinueOnError(). - NamespaceParam(c.namespace()). - DefaultNamespace(). - RequireNamespace(). Stream(reader, ""). - Flatten(). Do().Infos() return result, scrubValidationError(err) } @@ -146,7 +144,12 @@ func (c *Client) BuildUnstructured(namespace string, reader io.Reader) (Result, // Build validates for Kubernetes objects and returns resource Infos from a io.Reader. func (c *Client) Build(namespace string, reader io.Reader) (Result, error) { var result Result - result, err := c.newBuilder(namespace, reader).Infos() + result, err := c.newBuilder(). + WithScheme(legacyscheme.Scheme). + Schema(c.validator()). + Stream(reader, ""). + Do(). + Infos() return result, scrubValidationError(err) } @@ -162,7 +165,7 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) { return "", err } - var objPods = make(map[string][]core.Pod) + var objPods = make(map[string][]v1.Pod) missing := []string{} err = perform(infos, func(info *resource.Info) error { @@ -177,7 +180,7 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) { // versions per cluster, but this certainly won't hurt anything, so let's be safe. gvk := info.ResourceMapping().GroupVersionKind vk := gvk.Version + "/" + gvk.Kind - objs[vk] = append(objs[vk], info.AsInternal()) + objs[vk] = append(objs[vk], asVersioned(info)) //Get the relation pods objPods, err = c.getSelectRelationPod(info, objPods) @@ -203,10 +206,7 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) { // an object type changes, so we can just rely on that. Problem is it doesn't seem to keep // track of tab widths. buf := new(bytes.Buffer) - p, err := cmdutil.PrinterForOptions(&printers.PrintOptions{}) - if err != nil { - return "", err - } + p, _ := get.NewHumanPrintFlags().ToPrinter("") for t, ot := range objs { if _, err = buf.WriteString("==> " + t + "\n"); err != nil { return "", err @@ -294,7 +294,7 @@ func (c *Client) Update(namespace string, originalReader, targetReader io.Reader for _, info := range original.Difference(target) { c.Log("Deleting %q in %s...", info.Name, info.Namespace) - if err := deleteResource(c, info); err != nil { + if err := deleteResource(info); err != nil { c.Log("Failed to delete %q, err: %s", info.Name, err) } } @@ -314,7 +314,7 @@ func (c *Client) Delete(namespace string, reader io.Reader) error { } return perform(infos, func(info *resource.Info) error { c.Log("Starting delete for %q %s", info.Name, info.Mapping.GroupVersionKind.Kind) - err := deleteResource(c, info) + err := deleteResource(info) return c.skipIfNotFound(err) }) } @@ -369,24 +369,18 @@ func perform(infos Result, fn ResourceActorFunc) error { } func createResource(info *resource.Info) error { - obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object) + obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object, nil) if err != nil { return err } return info.Refresh(obj, true) } -func deleteResource(c *Client, info *resource.Info) error { - reaper, err := c.Reaper(info.Mapping) - if err != nil { - // If there is no reaper for this resources, delete it. - if kubectl.IsNoSuchReaperError(err) { - return resource.NewHelper(info.Client, info.Mapping).Delete(info.Namespace, info.Name) - } - return err - } - c.Log("Using reaper for deleting %q", info.Name) - return reaper.Stop(info.Namespace, info.Name, 0, nil) +func deleteResource(info *resource.Info) error { + policy := metav1.DeletePropagationBackground + opts := &metav1.DeleteOptions{PropagationPolicy: &policy} + _, err := resource.NewHelper(info.Client, info.Mapping).DeleteWithOptions(info.Namespace, info.Name, opts) + return err } func createPatch(target *resource.Info, current runtime.Object) ([]byte, types.PatchType, error) { @@ -408,7 +402,7 @@ func createPatch(target *resource.Info, current runtime.Object) ([]byte, types.P } // Get a versioned object - versionedObject, err := target.Versioned() + versionedObject := asVersioned(target) // Unstructured objects, such as CRDs, may not have an not registered error // returned from ConvertToVersion. Anything that's unstructured should @@ -445,14 +439,14 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, // send patch to server helper := resource.NewHelper(target.Client, target.Mapping) - obj, err := helper.Patch(target.Namespace, target.Name, patchType, patch) + obj, err := helper.Patch(target.Namespace, target.Name, patchType, patch, nil) if err != nil { kind := target.Mapping.GroupVersionKind.Kind log.Printf("Cannot patch %s: %q (%v)", kind, target.Name, err) if force { // Attempt to delete... - if err := deleteResource(c, target); err != nil { + if err := deleteResource(target); err != nil { return err } log.Printf("Deleted %s: %q", kind, target.Name) @@ -480,25 +474,18 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, return nil } - versioned, err := target.Versioned() - if runtime.IsNotRegisteredError(err) { - return nil - } - if err != nil { - return err - } - + versioned := asVersioned(target) selector, err := getSelectorFromObject(versioned) if err != nil { return nil } - client, err := c.ClientSet() + client, err := c.KubernetesClientSet() if err != nil { return err } - pods, err := client.Core().Pods(target.Namespace).List(metav1.ListOptions{ + pods, err := client.CoreV1().Pods(target.Namespace).List(metav1.ListOptions{ FieldSelector: fields.Everything().String(), LabelSelector: labels.Set(selector).AsSelector().String(), }) @@ -511,7 +498,7 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, c.Log("Restarting pod: %v/%v", pod.Namespace, pod.Name) // Delete each pod for get them restarted with changed spec. - if err := client.Core().Pods(pod.Namespace).Delete(pod.Name, metav1.NewPreconditionDeleteOptions(string(pod.UID))); err != nil { + if err := client.CoreV1().Pods(pod.Namespace).Delete(pod.Name, metav1.NewPreconditionDeleteOptions(string(pod.UID))); err != nil { return err } } @@ -575,7 +562,9 @@ func (c *Client) watchUntilReady(timeout time.Duration, info *resource.Info) err // In the future, we might want to add some special logic for types // like Ingress, Volume, etc. - _, err = watch.Until(timeout, w, func(e watch.Event) (bool, error) { + ctx, cancel := watchtools.ContextWithOptionalTimeout(context.Background(), timeout) + defer cancel() + _, err = watchtools.UntilWithoutRetry(ctx, w, func(e watch.Event) (bool, error) { switch e.Type { case watch.Added, watch.Modified: // For things like a secret or a config map, this is the best indicator @@ -611,9 +600,9 @@ func (c *Client) waitForJob(e watch.Event, name string) (bool, error) { } for _, c := range o.Status.Conditions { - if c.Type == batchinternal.JobComplete && c.Status == core.ConditionTrue { + if c.Type == batchinternal.JobComplete && c.Status == "True" { return true, nil - } else if c.Type == batchinternal.JobFailed && c.Status == core.ConditionTrue { + } else if c.Type == batchinternal.JobFailed && c.Status == "True" { return true, goerrors.Errorf("job failed: %s", c.Reason) } } @@ -637,26 +626,26 @@ func scrubValidationError(err error) error { // WaitAndGetCompletedPodPhase waits up to a timeout until a pod enters a completed phase // and returns said phase (PodSucceeded or PodFailed qualify). -func (c *Client) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (core.PodPhase, error) { +func (c *Client) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (v1.PodPhase, error) { infos, err := c.Build(namespace, reader) if err != nil { - return core.PodUnknown, err + return v1.PodUnknown, err } info := infos[0] kind := info.Mapping.GroupVersionKind.Kind if kind != "Pod" { - return core.PodUnknown, goerrors.Errorf("%s is not a Pod", info.Name) + return v1.PodUnknown, goerrors.Errorf("%s is not a Pod", info.Name) } if err := c.watchPodUntilComplete(timeout, info); err != nil { - return core.PodUnknown, err + return v1.PodUnknown, err } if err := info.Get(); err != nil { - return core.PodUnknown, err + return v1.PodUnknown, err } - status := info.Object.(*core.Pod).Status.Phase + status := info.Object.(*v1.Pod).Status.Phase return status, nil } @@ -668,15 +657,17 @@ func (c *Client) watchPodUntilComplete(timeout time.Duration, info *resource.Inf } c.Log("Watching pod %s for completion with timeout of %v", info.Name, timeout) - _, err = watch.Until(timeout, w, func(e watch.Event) (bool, error) { + ctx, cancel := watchtools.ContextWithOptionalTimeout(context.Background(), timeout) + defer cancel() + _, err = watchtools.UntilWithoutRetry(ctx, w, func(e watch.Event) (bool, error) { switch e.Type { case watch.Deleted: return false, errors.NewNotFound(schema.GroupResource{Resource: "pods"}, "") } switch t := e.Object.(type) { - case *core.Pod: + case *v1.Pod: switch t.Status.Phase { - case core.PodFailed, core.PodSucceeded: + case v1.PodFailed, v1.PodSucceeded: return true, nil } } @@ -688,20 +679,14 @@ func (c *Client) watchPodUntilComplete(timeout time.Duration, info *resource.Inf //get a kubernetes resources' relation pods // kubernetes resource used select labels to relate pods -func (c *Client) getSelectRelationPod(info *resource.Info, objPods map[string][]core.Pod) (map[string][]core.Pod, error) { +func (c *Client) getSelectRelationPod(info *resource.Info, objPods map[string][]v1.Pod) (map[string][]v1.Pod, error) { if info == nil { return objPods, nil } c.Log("get relation pod of object: %s/%s/%s", info.Namespace, info.Mapping.GroupVersionKind.Kind, info.Name) - versioned, err := info.Versioned() - if runtime.IsNotRegisteredError(err) { - return objPods, nil - } - if err != nil { - return objPods, err - } + versioned := asVersioned(info) // We can ignore this error because it will only error if it isn't a type that doesn't // have pods. In that case, we don't care @@ -714,9 +699,9 @@ func (c *Client) getSelectRelationPod(info *resource.Info, objPods map[string][] return objPods, nil } - client, _ := c.ClientSet() + client, _ := c.KubernetesClientSet() - pods, err := client.Core().Pods(info.Namespace).List(metav1.ListOptions{ + pods, err := client.CoreV1().Pods(info.Namespace).List(metav1.ListOptions{ FieldSelector: fields.Everything().String(), LabelSelector: labels.Set(selector).AsSelector().String(), }) @@ -741,7 +726,7 @@ func (c *Client) getSelectRelationPod(info *resource.Info, objPods map[string][] return objPods, nil } -func isFoundPod(podItem []core.Pod, pod core.Pod) bool { +func isFoundPod(podItem []v1.Pod, pod v1.Pod) bool { for _, value := range podItem { if (value.Namespace == pod.Namespace) && (value.Name == pod.Name) { return true diff --git a/pkg/kube/client_test.go b/pkg/kube/client_test.go index 97dcd3b90..84dc6fc6e 100644 --- a/pkg/kube/client_test.go +++ b/pkg/kube/client_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -23,58 +23,52 @@ import ( "net/http" "strings" "testing" - "time" - "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/dynamic" + "k8s.io/cli-runtime/pkg/genericclioptions/resource" "k8s.io/client-go/rest/fake" - "k8s.io/kubernetes/pkg/api/legacyscheme" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/kubectl" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" - "k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/kubectl/scheme" ) -var unstructuredSerializer = dynamic.ContentConfig().NegotiatedSerializer +var unstructuredSerializer = resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer +var codec = scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) -func objBody(codec runtime.Codec, obj runtime.Object) io.ReadCloser { +func objBody(obj runtime.Object) io.ReadCloser { return ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(codec, obj)))) } -func newPod(name string) core.Pod { - return newPodWithStatus(name, core.PodStatus{}, "") +func newPod(name string) v1.Pod { + return newPodWithStatus(name, v1.PodStatus{}, "") } -func newPodWithStatus(name string, status core.PodStatus, namespace string) core.Pod { - ns := core.NamespaceDefault +func newPodWithStatus(name string, status v1.PodStatus, namespace string) v1.Pod { + ns := v1.NamespaceDefault if namespace != "" { ns = namespace } - return core.Pod{ + return v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: ns, SelfLink: "/api/v1/namespaces/default/pods/" + name, }, - Spec: core.PodSpec{ - Containers: []core.Container{{ + Spec: v1.PodSpec{ + Containers: []v1.Container{{ Name: "app:v4", Image: "abc/app:v4", - Ports: []core.ContainerPort{{Name: "http", ContainerPort: 80}}, + Ports: []v1.ContainerPort{{Name: "http", ContainerPort: 80}}, }}, }, Status: status, } } -func newPodList(names ...string) core.PodList { - var list core.PodList +func newPodList(names ...string) v1.PodList { + var list v1.PodList for _, name := range names { list.Items = append(list.Items, newPod(name)) } @@ -94,28 +88,10 @@ func notFoundBody() *metav1.Status { func newResponse(code int, obj runtime.Object) (*http.Response, error) { header := http.Header{} header.Set("Content-Type", runtime.ContentTypeJSON) - body := ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), obj)))) + body := ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(codec, obj)))) return &http.Response{StatusCode: code, Header: header, Body: body}, nil } -type fakeReaper struct { - name string -} - -func (r *fakeReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *metav1.DeleteOptions) error { - r.name = name - return nil -} - -type fakeReaperFactory struct { - cmdutil.Factory - reaper kubectl.Reaper -} - -func (f *fakeReaperFactory) Reaper(mapping *meta.RESTMapping) (kubectl.Reaper, error) { - return f.reaper, nil -} - type testClient struct { *Client *cmdtesting.TestFactory @@ -123,8 +99,6 @@ type testClient struct { func newTestClient() *testClient { tf := cmdtesting.NewTestFactory() - tf.Namespace = core.NamespaceDefault - c := &Client{Factory: tf, Log: nopLogger} return &testClient{Client: c, TestFactory: tf} } @@ -133,15 +107,14 @@ func TestUpdate(t *testing.T) { listA := newPodList("starfish", "otter", "squid") listB := newPodList("starfish", "otter", "dolphin") listC := newPodList("starfish", "otter", "dolphin") - listB.Items[0].Spec.Containers[0].Ports = []core.ContainerPort{{Name: "https", ContainerPort: 443}} - listC.Items[0].Spec.Containers[0].Ports = []core.ContainerPort{{Name: "https", ContainerPort: 443}} + listB.Items[0].Spec.Containers[0].Ports = []v1.ContainerPort{{Name: "https", ContainerPort: 443}} + listC.Items[0].Spec.Containers[0].Ports = []v1.ContainerPort{{Name: "https", ContainerPort: 443}} var actions []string - tf := cmdtesting.NewTestFactory() + tf := cmdtesting.NewTestFactory().WithNamespace("default") defer tf.Cleanup() tf.UnstructuredClient = &fake.RESTClient{ - GroupVersion: schema.GroupVersion{Version: "v1"}, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { p, m := req.URL.Path, req.Method @@ -176,13 +149,11 @@ func TestUpdate(t *testing.T) { }), } - c := newTestClient() - tf.Namespace = core.NamespaceDefault - reaper := &fakeReaper{} - rf := &fakeReaperFactory{Factory: tf, reaper: reaper} - c.Client.Factory = rf - codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...) - if err := c.Update(core.NamespaceDefault, objBody(codec, &listA), objBody(codec, &listB), false, false, 0, false); err != nil { + c := &Client{ + Factory: tf, + Log: nopLogger, + } + if err := c.Update(v1.NamespaceDefault, objBody(&listA), objBody(&listB), false, false, 0, false); err != nil { t.Fatal(err) } // TODO: Find a way to test methods that use Client Set @@ -202,6 +173,7 @@ func TestUpdate(t *testing.T) { "/namespaces/default/pods/otter:GET", "/namespaces/default/pods/dolphin:GET", "/namespaces/default/pods:POST", + "/namespaces/default/pods/squid:DELETE", } if len(expectedActions) != len(actions) { t.Errorf("unexpected number of requests, expected %d, got %d", len(expectedActions), len(actions)) @@ -212,11 +184,6 @@ func TestUpdate(t *testing.T) { t.Errorf("expected %s request got %s", v, actions[k]) } } - - if reaper.name != "squid" { - t.Errorf("unexpected reaper: %#v", reaper) - } - } func TestBuild(t *testing.T) { diff --git a/pkg/kube/config.go b/pkg/kube/config.go index 5038f49c4..2e430d5ac 100644 --- a/pkg/kube/config.go +++ b/pkg/kube/config.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -16,17 +16,13 @@ limitations under the License. package kube // import "k8s.io/helm/pkg/kube" -import "k8s.io/client-go/tools/clientcmd" +import "k8s.io/cli-runtime/pkg/genericclioptions" // GetConfig returns a Kubernetes client config. -func GetConfig(kubeconfig, context, namespace string) clientcmd.ClientConfig { - rules := clientcmd.NewDefaultClientConfigLoadingRules() - rules.DefaultClientConfig = &clientcmd.DefaultClientConfig - rules.ExplicitPath = kubeconfig - - overrides := &clientcmd.ConfigOverrides{ClusterDefaults: clientcmd.ClusterDefaults} - overrides.CurrentContext = context - overrides.Context.Namespace = namespace - - return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(rules, overrides) +func GetConfig(kubeconfig, context, namespace string) *genericclioptions.ConfigFlags { + cf := genericclioptions.NewConfigFlags() + cf.Namespace = &namespace + cf.Context = &context + cf.KubeConfig = &kubeconfig + return cf } diff --git a/pkg/kube/converter.go b/pkg/kube/converter.go new file mode 100644 index 000000000..b056129e9 --- /dev/null +++ b/pkg/kube/converter.go @@ -0,0 +1,37 @@ +/* +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 kube // import "k8s.io/helm/pkg/kube" + +import ( + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/cli-runtime/pkg/genericclioptions/resource" + "k8s.io/kubernetes/pkg/api/legacyscheme" +) + +func asVersioned(info *resource.Info) runtime.Object { + converter := runtime.ObjectConvertor(legacyscheme.Scheme) + groupVersioner := runtime.GroupVersioner(schema.GroupVersions(legacyscheme.Scheme.PrioritizedVersionsAllGroups())) + if info.Mapping != nil { + groupVersioner = info.Mapping.GroupVersionKind.GroupVersion() + } + + if obj, err := converter.ConvertToVersion(info.Object, groupVersioner); err == nil { + return obj + } + return info.Object +} diff --git a/pkg/kube/factory.go b/pkg/kube/factory.go new file mode 100644 index 000000000..26dad8379 --- /dev/null +++ b/pkg/kube/factory.go @@ -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 kube // import "k8s.io/helm/pkg/kube" + +import ( + "k8s.io/cli-runtime/pkg/genericclioptions/resource" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/kubernetes/pkg/kubectl/validation" +) + +// Factory provides abstractions that allow the Kubectl command to be extended across multiple types +// of resources and different API sets. +type Factory interface { + // ToRawKubeConfigLoader return kubeconfig loader as-is + ToRawKubeConfigLoader() clientcmd.ClientConfig + // KubernetesClientSet gives you back an external clientset + KubernetesClientSet() (*kubernetes.Clientset, error) + // NewBuilder returns an object that assists in loading objects from both disk and the server + // and which implements the common patterns for CLI interactions with generic resources. + NewBuilder() *resource.Builder + // Returns a schema that can validate objects stored on disk. + Validator(validate bool) (validation.Schema, error) +} diff --git a/pkg/kube/log.go b/pkg/kube/log.go index fbe51823a..24dafc9e0 100644 --- a/pkg/kube/log.go +++ b/pkg/kube/log.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -24,7 +24,7 @@ import ( func init() { if level := os.Getenv("KUBE_LOG_LEVEL"); level != "" { - flag.Set("vmodule", fmt.Sprintf("loader=%s,round_trippers=%s,request=%s", level, level, level)) + flag.Set("vmodule", fmt.Sprintf("loader=%[1]s,round_trippers=%[1]s,request=%[1]s", level)) flag.Set("logtostderr", "true") } } diff --git a/pkg/kube/result.go b/pkg/kube/result.go index 87c7e6ac1..cc222a66f 100644 --- a/pkg/kube/result.go +++ b/pkg/kube/result.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -16,7 +16,7 @@ limitations under the License. package kube // import "k8s.io/helm/pkg/kube" -import "k8s.io/kubernetes/pkg/kubectl/resource" +import "k8s.io/cli-runtime/pkg/genericclioptions/resource" // Result provides convenience methods for comparing collections of Infos. type Result []*resource.Info diff --git a/pkg/kube/result_test.go b/pkg/kube/result_test.go index 962e90426..c4cf989b8 100644 --- a/pkg/kube/result_test.go +++ b/pkg/kube/result_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -19,15 +19,14 @@ package kube // import "k8s.io/helm/pkg/kube" import ( "testing" + "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/cli-runtime/pkg/genericclioptions/resource" ) func TestResult(t *testing.T) { - mapping, err := testapi.Default.RESTMapper().RESTMapping(schema.GroupKind{Kind: "Pod"}) - if err != nil { - t.Fatal(err) + mapping := &meta.RESTMapping{ + Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "pod"}, } info := func(name string) *resource.Info { diff --git a/pkg/kube/wait.go b/pkg/kube/wait.go index 88f3c7d34..ebd12b4b1 100644 --- a/pkg/kube/wait.go +++ b/pkg/kube/wait.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -27,18 +27,15 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/kubernetes" - podutil "k8s.io/kubernetes/pkg/api/v1/pod" - "k8s.io/kubernetes/pkg/apis/core/v1/helper" deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util" ) // deployment holds associated replicaSets for a deployment type deployment struct { - replicaSets *extensions.ReplicaSet - deployment *extensions.Deployment + replicaSets *appsv1.ReplicaSet + deployment *appsv1.Deployment } // waitForResources polls to get the current status of all pods, PVCs, and Services @@ -51,16 +48,14 @@ func (c *Client) waitForResources(timeout time.Duration, created Result) error { return err } return wait.Poll(2*time.Second, timeout, func() (bool, error) { - pods := []v1.Pod{} - services := []v1.Service{} - pvc := []v1.PersistentVolumeClaim{} - deployments := []deployment{} - for _, v := range created { - obj, err := v.Versioned() - if err != nil && !runtime.IsNotRegisteredError(err) { - return false, err - } - switch value := obj.(type) { + var ( + pods []v1.Pod + services []v1.Service + pvc []v1.PersistentVolumeClaim + deployments []deployment + ) + for _, v := range created[:0] { + switch value := asVersioned(v).(type) { case *v1.ReplicationController: list, err := getPods(kcs, value.Namespace, value.Spec.Selector) if err != nil { @@ -74,12 +69,12 @@ func (c *Client) waitForResources(timeout time.Duration, created Result) error { } pods = append(pods, *pod) case *appsv1.Deployment: - currentDeployment, err := kcs.ExtensionsV1beta1().Deployments(value.Namespace).Get(value.Name, metav1.GetOptions{}) + currentDeployment, err := kcs.AppsV1().Deployments(value.Namespace).Get(value.Name, metav1.GetOptions{}) if err != nil { return false, err } // Find RS associated with deployment - newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.ExtensionsV1beta1()) + newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.AppsV1()) if err != nil || newReplicaSet == nil { return false, err } @@ -89,12 +84,12 @@ func (c *Client) waitForResources(timeout time.Duration, created Result) error { } deployments = append(deployments, newDeployment) case *appsv1beta1.Deployment: - currentDeployment, err := kcs.ExtensionsV1beta1().Deployments(value.Namespace).Get(value.Name, metav1.GetOptions{}) + currentDeployment, err := kcs.AppsV1().Deployments(value.Namespace).Get(value.Name, metav1.GetOptions{}) if err != nil { return false, err } // Find RS associated with deployment - newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.ExtensionsV1beta1()) + newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.AppsV1()) if err != nil || newReplicaSet == nil { return false, err } @@ -104,12 +99,12 @@ func (c *Client) waitForResources(timeout time.Duration, created Result) error { } deployments = append(deployments, newDeployment) case *appsv1beta2.Deployment: - currentDeployment, err := kcs.ExtensionsV1beta1().Deployments(value.Namespace).Get(value.Name, metav1.GetOptions{}) + currentDeployment, err := kcs.AppsV1().Deployments(value.Namespace).Get(value.Name, metav1.GetOptions{}) if err != nil { return false, err } // Find RS associated with deployment - newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.ExtensionsV1beta1()) + newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.AppsV1()) if err != nil || newReplicaSet == nil { return false, err } @@ -119,12 +114,12 @@ func (c *Client) waitForResources(timeout time.Duration, created Result) error { } deployments = append(deployments, newDeployment) case *extensions.Deployment: - currentDeployment, err := kcs.ExtensionsV1beta1().Deployments(value.Namespace).Get(value.Name, metav1.GetOptions{}) + currentDeployment, err := kcs.AppsV1().Deployments(value.Namespace).Get(value.Name, metav1.GetOptions{}) if err != nil { return false, err } // Find RS associated with deployment - newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.ExtensionsV1beta1()) + newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.AppsV1()) if err != nil || newReplicaSet == nil { return false, err } @@ -208,7 +203,7 @@ func (c *Client) waitForResources(timeout time.Duration, created Result) error { func (c *Client) podsReady(pods []v1.Pod) bool { for _, pod := range pods { - if !podutil.IsPodReady(&pod) { + if !IsPodReady(&pod) { c.Log("Pod is not ready: %s/%s", pod.GetNamespace(), pod.GetName()) return false } @@ -216,6 +211,16 @@ func (c *Client) podsReady(pods []v1.Pod) bool { return true } +// IsPodReady returns true if a pod is ready; false otherwise. +func IsPodReady(pod *v1.Pod) bool { + for _, c := range pod.Status.Conditions { + if c.Type == v1.PodReady && c.Status == v1.ConditionTrue { + return true + } + } + return false +} + func (c *Client) servicesReady(svc []v1.Service) bool { for _, s := range svc { // ExternalName Services are external to cluster so helm shouldn't be checking to see if they're 'ready' (i.e. have an IP Set) @@ -224,7 +229,7 @@ func (c *Client) servicesReady(svc []v1.Service) bool { } // Make sure the service is not explicitly set to "None" before checking the IP - if s.Spec.ClusterIP != v1.ClusterIPNone && !helper.IsServiceIPSet(&s) { + if s.Spec.ClusterIP != v1.ClusterIPNone && !IsServiceIPSet(&s) { c.Log("Service is not ready: %s/%s", s.GetNamespace(), s.GetName()) return false } @@ -237,6 +242,12 @@ func (c *Client) servicesReady(svc []v1.Service) bool { return true } +// this function aims to check if the service's ClusterIP is set or not +// the objective is not to perform validation here +func IsServiceIPSet(service *v1.Service) bool { + return service.Spec.ClusterIP != v1.ClusterIPNone && service.Spec.ClusterIP != "" +} + func (c *Client) volumesReady(vols []v1.PersistentVolumeClaim) bool { for _, v := range vols { if v.Status.Phase != v1.ClaimBound { diff --git a/pkg/lint/lint.go b/pkg/lint/lint.go index 256eab906..aa8df5814 100644 --- a/pkg/lint/lint.go +++ b/pkg/lint/lint.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/lint/lint_test.go b/pkg/lint/lint_test.go index d84faa10b..2f0b88526 100644 --- a/pkg/lint/lint_test.go +++ b/pkg/lint/lint_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -82,7 +82,7 @@ func TestInvalidYaml(t *testing.T) { func TestBadValues(t *testing.T) { m := All(badValuesFileDir, values, namespace, strict).Messages - if len(m) != 1 { + if len(m) < 1 { t.Fatalf("All didn't fail with expected errors, got %#v", m) } if !strings.Contains(m[0].Err.Error(), "cannot unmarshal") { diff --git a/pkg/lint/rules/chartfile.go b/pkg/lint/rules/chartfile.go index 30691c500..7a2cbed20 100644 --- a/pkg/lint/rules/chartfile.go +++ b/pkg/lint/rules/chartfile.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -24,8 +24,8 @@ import ( "github.com/asaskevich/govalidator" "github.com/pkg/errors" + "k8s.io/helm/pkg/chart" "k8s.io/helm/pkg/chartutil" - "k8s.io/helm/pkg/hapi/chart" "k8s.io/helm/pkg/lint/support" ) diff --git a/pkg/lint/rules/chartfile_test.go b/pkg/lint/rules/chartfile_test.go index 4af73422e..a07a31413 100644 --- a/pkg/lint/rules/chartfile_test.go +++ b/pkg/lint/rules/chartfile_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -24,8 +24,8 @@ import ( "github.com/pkg/errors" + "k8s.io/helm/pkg/chart" "k8s.io/helm/pkg/chartutil" - "k8s.io/helm/pkg/hapi/chart" "k8s.io/helm/pkg/lint/support" ) diff --git a/pkg/lint/rules/template.go b/pkg/lint/rules/template.go index 6b2a75027..7d0da4782 100644 --- a/pkg/lint/rules/template.go +++ b/pkg/lint/rules/template.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -23,10 +23,10 @@ import ( "github.com/ghodss/yaml" "github.com/pkg/errors" + "k8s.io/helm/pkg/chart/loader" "k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/engine" "k8s.io/helm/pkg/lint/support" - tversion "k8s.io/helm/pkg/version" ) // Templates lints the templates in the Linter. @@ -42,7 +42,7 @@ func Templates(linter *support.Linter, values []byte, namespace string, strict b } // Load chart and parse templates, based on tiller/release_server - chart, err := chartutil.Load(linter.ChartDir) + chart, err := loader.Load(linter.ChartDir) chartLoaded := linter.RunLinterRule(support.ErrorSev, path, err) @@ -51,11 +51,7 @@ func Templates(linter *support.Linter, values []byte, namespace string, strict b } options := chartutil.ReleaseOptions{Name: "testRelease"} - caps := &chartutil.Capabilities{ - APIVersions: chartutil.DefaultVersionSet, - KubeVersion: chartutil.DefaultKubeVersion, - HelmVersion: tversion.GetBuildInfo(), - } + cvals, err := chartutil.CoalesceValues(chart, values) if err != nil { return @@ -65,7 +61,8 @@ func Templates(linter *support.Linter, values []byte, namespace string, strict b if err != nil { return } - valuesToRender, err := chartutil.ToRenderValuesCaps(chart, yvals, options, caps) + caps := chartutil.DefaultCapabilities + valuesToRender, err := chartutil.ToRenderValues(chart, yvals, options, caps) if err != nil { // FIXME: This seems to generate a duplicate, but I can't find where the first // error is coming from. @@ -109,7 +106,7 @@ func Templates(linter *support.Linter, values []byte, namespace string, strict b // NOTE: disabled for now, Refs https://github.com/kubernetes/helm/issues/1037 // linter.RunLinterRule(support.WarningSev, path, validateQuotes(string(preExecutedTemplate))) - renderedContent := renderedContentMap[filepath.Join(chart.Metadata.Name, fileName)] + renderedContent := renderedContentMap[filepath.Join(chart.Name(), fileName)] var yamlStruct K8sYamlStruct // Even though K8sYamlStruct only defines Metadata namespace, an error in any other // key will be raised as well diff --git a/pkg/lint/rules/template_test.go b/pkg/lint/rules/template_test.go index cb1be94a2..41a7384e7 100644 --- a/pkg/lint/rules/template_test.go +++ b/pkg/lint/rules/template_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/lint/rules/values.go b/pkg/lint/rules/values.go index 6404dca1e..d698cea71 100644 --- a/pkg/lint/rules/values.go +++ b/pkg/lint/rules/values.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/lint/support/doc.go b/pkg/lint/support/doc.go index 4cf7272e4..ede608906 100644 --- a/pkg/lint/support/doc.go +++ b/pkg/lint/support/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/lint/support/message.go b/pkg/lint/support/message.go index 6a878031a..4dd485c98 100644 --- a/pkg/lint/support/message.go +++ b/pkg/lint/support/message.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/lint/support/message_test.go b/pkg/lint/support/message_test.go index 782642099..9e12a638b 100644 --- a/pkg/lint/support/message_test.go +++ b/pkg/lint/support/message_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/plugin/cache/cache.go b/pkg/plugin/cache/cache.go index a1d3224c8..d846126f1 100644 --- a/pkg/plugin/cache/cache.go +++ b/pkg/plugin/cache/cache.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/plugin/hooks.go b/pkg/plugin/hooks.go index a779b6686..82636fbbe 100644 --- a/pkg/plugin/hooks.go +++ b/pkg/plugin/hooks.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/plugin/installer/base.go b/pkg/plugin/installer/base.go index 0664dae76..15ce3cbcf 100644 --- a/pkg/plugin/installer/base.go +++ b/pkg/plugin/installer/base.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/plugin/installer/doc.go b/pkg/plugin/installer/doc.go index a2a66f3e1..0089e33f8 100644 --- a/pkg/plugin/installer/doc.go +++ b/pkg/plugin/installer/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/plugin/installer/http_installer.go b/pkg/plugin/installer/http_installer.go index 80c560147..b4103d60a 100644 --- a/pkg/plugin/installer/http_installer.go +++ b/pkg/plugin/installer/http_installer.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/plugin/installer/http_installer_test.go b/pkg/plugin/installer/http_installer_test.go index 0020487b5..4eea99e9f 100644 --- a/pkg/plugin/installer/http_installer_test.go +++ b/pkg/plugin/installer/http_installer_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/plugin/installer/installer.go b/pkg/plugin/installer/installer.go index a377ab28f..48cea1ac8 100644 --- a/pkg/plugin/installer/installer.go +++ b/pkg/plugin/installer/installer.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/plugin/installer/local_installer.go b/pkg/plugin/installer/local_installer.go index bc6266981..82f0f7e2f 100644 --- a/pkg/plugin/installer/local_installer.go +++ b/pkg/plugin/installer/local_installer.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/plugin/installer/local_installer_test.go b/pkg/plugin/installer/local_installer_test.go index 6a7c957d6..fb5fa2675 100644 --- a/pkg/plugin/installer/local_installer_test.go +++ b/pkg/plugin/installer/local_installer_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/plugin/installer/vcs_installer.go b/pkg/plugin/installer/vcs_installer.go index e57ba1224..211f46481 100644 --- a/pkg/plugin/installer/vcs_installer.go +++ b/pkg/plugin/installer/vcs_installer.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/plugin/installer/vcs_installer_test.go b/pkg/plugin/installer/vcs_installer_test.go index 453899543..31dc24685 100644 --- a/pkg/plugin/installer/vcs_installer_test.go +++ b/pkg/plugin/installer/vcs_installer_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index 5a3c84892..f5cd3efb7 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/plugin/plugin_test.go b/pkg/plugin/plugin_test.go index 51e480b95..6327f32e9 100644 --- a/pkg/plugin/plugin_test.go +++ b/pkg/plugin/plugin_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/provenance/doc.go b/pkg/provenance/doc.go index dacfa9e69..bee484944 100644 --- a/pkg/provenance/doc.go +++ b/pkg/provenance/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/provenance/sign.go b/pkg/provenance/sign.go index 4d1803454..23dcf2bcf 100644 --- a/pkg/provenance/sign.go +++ b/pkg/provenance/sign.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 @@ -31,8 +31,8 @@ import ( "golang.org/x/crypto/openpgp/clearsign" "golang.org/x/crypto/openpgp/packet" - "k8s.io/helm/pkg/chartutil" - hapi "k8s.io/helm/pkg/hapi/chart" + hapi "k8s.io/helm/pkg/chart" + "k8s.io/helm/pkg/chart/loader" ) var defaultPGPConfig = packet.Config{ @@ -317,7 +317,7 @@ func messageBlock(chartpath string) (*bytes.Buffer, error) { } // Load the archive into memory. - chart, err := chartutil.LoadFile(chartpath) + chart, err := loader.LoadFile(chartpath) if err != nil { return b, err } diff --git a/pkg/provenance/sign_test.go b/pkg/provenance/sign_test.go index 388941deb..d74e23887 100644 --- a/pkg/provenance/sign_test.go +++ b/pkg/provenance/sign_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/releasetesting/environment.go b/pkg/releasetesting/environment.go index 62b8f119e..fcb52e84d 100644 --- a/pkg/releasetesting/environment.go +++ b/pkg/releasetesting/environment.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -22,7 +22,7 @@ import ( "log" "time" - "k8s.io/kubernetes/pkg/apis/core" + "k8s.io/api/core/v1" "k8s.io/helm/pkg/hapi" "k8s.io/helm/pkg/hapi/release" @@ -48,7 +48,7 @@ func (env *Environment) createTestPod(test *test) error { return nil } -func (env *Environment) getTestPodStatus(test *test) (core.PodPhase, error) { +func (env *Environment) getTestPodStatus(test *test) (v1.PodPhase, error) { b := bytes.NewBufferString(test.manifest) status, err := env.KubeClient.WaitAndGetCompletedPodPhase(env.Namespace, b, time.Duration(env.Timeout)*time.Second) if err != nil { diff --git a/pkg/releasetesting/environment_test.go b/pkg/releasetesting/environment_test.go index ee290f5c7..08e385c00 100644 --- a/pkg/releasetesting/environment_test.go +++ b/pkg/releasetesting/environment_test.go @@ -1,5 +1,5 @@ /* -Copyright 2017 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/releasetesting/test_suite.go b/pkg/releasetesting/test_suite.go index b8a7fc8a1..590f01370 100644 --- a/pkg/releasetesting/test_suite.go +++ b/pkg/releasetesting/test_suite.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -22,7 +22,7 @@ import ( "github.com/ghodss/yaml" "github.com/pkg/errors" - "k8s.io/kubernetes/pkg/apis/core" + "k8s.io/api/core/v1" "k8s.io/helm/pkg/hapi/release" "k8s.io/helm/pkg/hooks" @@ -82,7 +82,7 @@ func (ts *TestSuite) Run(env *Environment) error { } resourceCleanExit := true - status := core.PodUnknown + status := v1.PodUnknown if resourceCreated { status, err = env.getTestPodStatus(test) if err != nil { @@ -111,15 +111,15 @@ func (ts *TestSuite) Run(env *Environment) error { return nil } -func (t *test) assignTestResult(podStatus core.PodPhase) error { +func (t *test) assignTestResult(podStatus v1.PodPhase) error { switch podStatus { - case core.PodSucceeded: + case v1.PodSucceeded: if t.expectedSuccess { t.result.Status = release.TestRunSuccess } else { t.result.Status = release.TestRunFailure } - case core.PodFailed: + case v1.PodFailed: if !t.expectedSuccess { t.result.Status = release.TestRunSuccess } else { diff --git a/pkg/releasetesting/test_suite_test.go b/pkg/releasetesting/test_suite_test.go index f90d426fa..4e8d152a6 100644 --- a/pkg/releasetesting/test_suite_test.go +++ b/pkg/releasetesting/test_suite_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -21,7 +21,7 @@ import ( "testing" "time" - "k8s.io/kubernetes/pkg/apis/core" + "k8s.io/api/core/v1" "k8s.io/helm/pkg/hapi" "k8s.io/helm/pkg/hapi/release" @@ -250,11 +250,11 @@ type mockKubeClient struct { err error } -func (c *mockKubeClient) WaitAndGetCompletedPodPhase(_ string, _ io.Reader, _ time.Duration) (core.PodPhase, error) { +func (c *mockKubeClient) WaitAndGetCompletedPodPhase(_ string, _ io.Reader, _ time.Duration) (v1.PodPhase, error) { if c.podFail { - return core.PodFailed, nil + return v1.PodFailed, nil } - return core.PodSucceeded, nil + return v1.PodSucceeded, nil } func (c *mockKubeClient) Get(_ string, _ io.Reader) (string, error) { return "", nil diff --git a/pkg/releaseutil/filter.go b/pkg/releaseutil/filter.go index 3ed825442..96a82ff93 100644 --- a/pkg/releaseutil/filter.go +++ b/pkg/releaseutil/filter.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/releaseutil/filter_test.go b/pkg/releaseutil/filter_test.go index 0eb6a32c3..c0ce85b90 100644 --- a/pkg/releaseutil/filter_test.go +++ b/pkg/releaseutil/filter_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -23,24 +23,24 @@ import ( ) func TestFilterAny(t *testing.T) { - ls := Any(StatusFilter(rspb.StatusDeleted)).Filter(releases) + ls := Any(StatusFilter(rspb.StatusUninstalled)).Filter(releases) if len(ls) != 2 { t.Fatalf("expected 2 results, got '%d'", len(ls)) } r0, r1 := ls[0], ls[1] switch { - case r0.Info.Status != rspb.StatusDeleted: - t.Fatalf("expected DELETED result, got '%s'", r1.Info.Status.String()) - case r1.Info.Status != rspb.StatusDeleted: - t.Fatalf("expected DELETED result, got '%s'", r1.Info.Status.String()) + case r0.Info.Status != rspb.StatusUninstalled: + t.Fatalf("expected UNINSTALLED result, got '%s'", r1.Info.Status.String()) + case r1.Info.Status != rspb.StatusUninstalled: + t.Fatalf("expected UNINSTALLED result, got '%s'", r1.Info.Status.String()) } } func TestFilterAll(t *testing.T) { fn := FilterFunc(func(rls *rspb.Release) bool { - // true if not deleted and version < 4 - v0 := !StatusFilter(rspb.StatusDeleted).Check(rls) + // true if not uninstalled and version < 4 + v0 := !StatusFilter(rspb.StatusUninstalled).Check(rls) v1 := rls.Version < 4 return v0 && v1 }) @@ -53,7 +53,7 @@ func TestFilterAll(t *testing.T) { switch r0 := ls[0]; { case r0.Version == 4: t.Fatal("got release with status revision 4") - case r0.Info.Status == rspb.StatusDeleted: - t.Fatal("got release with status DELTED") + case r0.Info.Status == rspb.StatusUninstalled: + t.Fatal("got release with status UNINSTALLED") } } diff --git a/pkg/releaseutil/manifest.go b/pkg/releaseutil/manifest.go index a0449cc55..37503b390 100644 --- a/pkg/releaseutil/manifest.go +++ b/pkg/releaseutil/manifest.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -46,7 +46,6 @@ func SplitManifests(bigFile string) map[string]string { docs := sep.Split(bigFileTmp, -1) var count int for _, d := range docs { - if d == "" { continue } diff --git a/pkg/releaseutil/manifest_test.go b/pkg/releaseutil/manifest_test.go index 7906279ad..8e0793d5f 100644 --- a/pkg/releaseutil/manifest_test.go +++ b/pkg/releaseutil/manifest_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/releaseutil/sorter.go b/pkg/releaseutil/sorter.go index cd90e5816..6106319df 100644 --- a/pkg/releaseutil/sorter.go +++ b/pkg/releaseutil/sorter.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -22,14 +22,28 @@ import ( rspb "k8s.io/helm/pkg/hapi/release" ) -type sorter struct { - list []*rspb.Release - less func(int, int) bool +type list []*rspb.Release + +func (s list) Len() int { return len(s) } +func (s list) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +type ByName struct{ list } + +func (s ByName) Less(i, j int) bool { return s.list[i].Name < s.list[j].Name } + +type ByDate struct{ list } + +func (s ByDate) Less(i, j int) bool { + ti := s.list[i].Info.LastDeployed.Second() + tj := s.list[j].Info.LastDeployed.Second() + return ti < tj } -func (s *sorter) Len() int { return len(s.list) } -func (s *sorter) Less(i, j int) bool { return s.less(i, j) } -func (s *sorter) Swap(i, j int) { s.list[i], s.list[j] = s.list[j], s.list[i] } +type ByRevision struct{ list } + +func (s ByRevision) Less(i, j int) bool { + return s.list[i].Version < s.list[j].Version +} // Reverse reverses the list of releases sorted by the sort func. func Reverse(list []*rspb.Release, sortFn func([]*rspb.Release)) { @@ -42,36 +56,17 @@ func Reverse(list []*rspb.Release, sortFn func([]*rspb.Release)) { // SortByName returns the list of releases sorted // in lexicographical order. func SortByName(list []*rspb.Release) { - s := &sorter{list: list} - s.less = func(i, j int) bool { - ni := s.list[i].Name - nj := s.list[j].Name - return ni < nj - } - sort.Sort(s) + sort.Sort(ByName{list}) } // SortByDate returns the list of releases sorted by a // release's last deployed time (in seconds). func SortByDate(list []*rspb.Release) { - s := &sorter{list: list} - - s.less = func(i, j int) bool { - ti := s.list[i].Info.LastDeployed.Second() - tj := s.list[j].Info.LastDeployed.Second() - return ti < tj - } - sort.Sort(s) + sort.Sort(ByDate{list}) } // SortByRevision returns the list of releases sorted by a // release's revision number (release.Version). func SortByRevision(list []*rspb.Release) { - s := &sorter{list: list} - s.less = func(i, j int) bool { - vi := s.list[i].Version - vj := s.list[j].Version - return vi < vj - } - sort.Sort(s) + sort.Sort(ByRevision{list}) } diff --git a/pkg/releaseutil/sorter_test.go b/pkg/releaseutil/sorter_test.go index 8761a6064..873a0de72 100644 --- a/pkg/releaseutil/sorter_test.go +++ b/pkg/releaseutil/sorter_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -28,8 +28,8 @@ import ( var releases = []*rspb.Release{ tsRelease("quiet-bear", 2, 2000, rspb.StatusSuperseded), tsRelease("angry-bird", 4, 3000, rspb.StatusDeployed), - tsRelease("happy-cats", 1, 4000, rspb.StatusDeleted), - tsRelease("vocal-dogs", 3, 6000, rspb.StatusDeleted), + tsRelease("happy-cats", 1, 4000, rspb.StatusUninstalled), + tsRelease("vocal-dogs", 3, 6000, rspb.StatusUninstalled), } func tsRelease(name string, vers int, dur time.Duration, status rspb.ReleaseStatus) *rspb.Release { diff --git a/pkg/repo/chartrepo.go b/pkg/repo/chartrepo.go index 708b15043..e211729b0 100644 --- a/pkg/repo/chartrepo.go +++ b/pkg/repo/chartrepo.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -27,7 +27,7 @@ import ( "github.com/ghodss/yaml" "github.com/pkg/errors" - "k8s.io/helm/pkg/chartutil" + "k8s.io/helm/pkg/chart/loader" "k8s.io/helm/pkg/getter" "k8s.io/helm/pkg/provenance" ) @@ -172,7 +172,7 @@ func (r *ChartRepository) saveIndexFile() error { func (r *ChartRepository) generateIndex() error { for _, path := range r.ChartPaths { - ch, err := chartutil.Load(path) + ch, err := loader.Load(path) if err != nil { return err } @@ -182,7 +182,7 @@ func (r *ChartRepository) generateIndex() error { return err } - if !r.IndexFile.Has(ch.Metadata.Name, ch.Metadata.Version) { + if !r.IndexFile.Has(ch.Name(), ch.Metadata.Version) { r.IndexFile.Add(ch.Metadata, path, r.Config.URL, digest) } // TODO: If a chart exists, but has a different Digest, should we error? diff --git a/pkg/repo/chartrepo_test.go b/pkg/repo/chartrepo_test.go index b1c066ced..53ae0a4eb 100644 --- a/pkg/repo/chartrepo_test.go +++ b/pkg/repo/chartrepo_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -27,8 +27,8 @@ import ( "testing" "time" + "k8s.io/helm/pkg/chart" "k8s.io/helm/pkg/getter" - "k8s.io/helm/pkg/hapi/chart" "k8s.io/helm/pkg/helm/environment" ) diff --git a/pkg/repo/doc.go b/pkg/repo/doc.go index fb8b3f4b2..19ccf267c 100644 --- a/pkg/repo/doc.go +++ b/pkg/repo/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/repo/index.go b/pkg/repo/index.go index 9cd6159fc..68a6d75b5 100644 --- a/pkg/repo/index.go +++ b/pkg/repo/index.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -30,8 +30,8 @@ import ( "github.com/ghodss/yaml" "github.com/pkg/errors" - "k8s.io/helm/pkg/chartutil" - "k8s.io/helm/pkg/hapi/chart" + "k8s.io/helm/pkg/chart" + "k8s.io/helm/pkg/chart/loader" "k8s.io/helm/pkg/provenance" "k8s.io/helm/pkg/urlutil" ) @@ -251,7 +251,7 @@ func IndexDirectory(dir, baseURL string) (*IndexFile, error) { parentURL = filepath.Join(baseURL, parentDir) } - c, err := chartutil.Load(arch) + c, err := loader.Load(arch) if err != nil { // Assume this is not a chart. continue diff --git a/pkg/repo/index_test.go b/pkg/repo/index_test.go index c3199290a..1ad7ebcc6 100644 --- a/pkg/repo/index_test.go +++ b/pkg/repo/index_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -22,8 +22,8 @@ import ( "path/filepath" "testing" + "k8s.io/helm/pkg/chart" "k8s.io/helm/pkg/getter" - "k8s.io/helm/pkg/hapi/chart" "k8s.io/helm/pkg/helm/environment" ) diff --git a/pkg/repo/repo.go b/pkg/repo/repo.go index c8789d0e7..46ce5c148 100644 --- a/pkg/repo/repo.go +++ b/pkg/repo/repo.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/repo/repo_test.go b/pkg/repo/repo_test.go index 97f49775c..c04390bf5 100644 --- a/pkg/repo/repo_test.go +++ b/pkg/repo/repo_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/repo/repotest/doc.go b/pkg/repo/repotest/doc.go index 34d4bc6b0..3bf98aa7e 100644 --- a/pkg/repo/repotest/doc.go +++ b/pkg/repo/repotest/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/repo/repotest/server.go b/pkg/repo/repotest/server.go index 8ea9103a0..36ab10d70 100644 --- a/pkg/repo/repotest/server.go +++ b/pkg/repo/repotest/server.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/repo/repotest/server_test.go b/pkg/repo/repotest/server_test.go index 61c056172..e4819fbf7 100644 --- a/pkg/repo/repotest/server_test.go +++ b/pkg/repo/repotest/server_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/resolver/resolver.go b/pkg/resolver/resolver.go index 2d2fba018..367529f0c 100644 --- a/pkg/resolver/resolver.go +++ b/pkg/resolver/resolver.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 @@ -26,7 +26,7 @@ import ( "github.com/Masterminds/semver" "github.com/pkg/errors" - "k8s.io/helm/pkg/chartutil" + "k8s.io/helm/pkg/chart" "k8s.io/helm/pkg/helm/helmpath" "k8s.io/helm/pkg/provenance" "k8s.io/helm/pkg/repo" @@ -47,19 +47,19 @@ func New(chartpath string, helmhome helmpath.Home) *Resolver { } // Resolve resolves dependencies and returns a lock file with the resolution. -func (r *Resolver) Resolve(reqs *chartutil.Requirements, repoNames map[string]string, d string) (*chartutil.RequirementsLock, error) { +func (r *Resolver) Resolve(reqs []*chart.Dependency, repoNames map[string]string, d string) (*chart.Lock, error) { // Now we clone the dependencies, locking as we go. - locked := make([]*chartutil.Dependency, len(reqs.Dependencies)) + locked := make([]*chart.Dependency, len(reqs)) missing := []string{} - for i, d := range reqs.Dependencies { + for i, d := range reqs { if strings.HasPrefix(d.Repository, "file://") { if _, err := GetLocalPath(d.Repository, r.chartpath); err != nil { return nil, err } - locked[i] = &chartutil.Dependency{ + locked[i] = &chart.Dependency{ Name: d.Name, Repository: d.Repository, Version: d.Version, @@ -81,7 +81,7 @@ func (r *Resolver) Resolve(reqs *chartutil.Requirements, repoNames map[string]st return nil, errors.Errorf("%s chart not found in repo %s", d.Name, d.Repository) } - locked[i] = &chartutil.Dependency{ + locked[i] = &chart.Dependency{ Name: d.Name, Repository: d.Repository, } @@ -105,9 +105,9 @@ func (r *Resolver) Resolve(reqs *chartutil.Requirements, repoNames map[string]st } } if len(missing) > 0 { - return nil, errors.Errorf("can't get a valid version for repositories %s. Try changing the version constraint in requirements.yaml", strings.Join(missing, ", ")) + return nil, errors.Errorf("can't get a valid version for repositories %s. Try changing the version constraint in Chart.yaml", strings.Join(missing, ", ")) } - return &chartutil.RequirementsLock{ + return &chart.Lock{ Generated: time.Now(), Digest: d, Dependencies: locked, @@ -118,7 +118,7 @@ func (r *Resolver) Resolve(reqs *chartutil.Requirements, repoNames map[string]st // // This should be used only to compare against another hash generated by this // function. -func HashReq(req *chartutil.Requirements) (string, error) { +func HashReq(req []*chart.Dependency) (string, error) { data, err := json.Marshal(req) if err != nil { return "", err diff --git a/pkg/resolver/resolver_test.go b/pkg/resolver/resolver_test.go index 78a0bc46c..7ec0cd387 100644 --- a/pkg/resolver/resolver_test.go +++ b/pkg/resolver/resolver_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 @@ -18,84 +18,70 @@ package resolver import ( "testing" - "k8s.io/helm/pkg/chartutil" + "k8s.io/helm/pkg/chart" ) func TestResolve(t *testing.T) { tests := []struct { name string - req *chartutil.Requirements - expect *chartutil.RequirementsLock + req []*chart.Dependency + expect *chart.Lock err bool }{ { name: "version failure", - req: &chartutil.Requirements{ - Dependencies: []*chartutil.Dependency{ - {Name: "oedipus-rex", Repository: "http://example.com", Version: ">a1"}, - }, + req: []*chart.Dependency{ + {Name: "oedipus-rex", Repository: "http://example.com", Version: ">a1"}, }, err: true, }, { name: "cache index failure", - req: &chartutil.Requirements{ - Dependencies: []*chartutil.Dependency{ - {Name: "oedipus-rex", Repository: "http://example.com", Version: "1.0.0"}, - }, + req: []*chart.Dependency{ + {Name: "oedipus-rex", Repository: "http://example.com", Version: "1.0.0"}, }, err: true, }, { name: "chart not found failure", - req: &chartutil.Requirements{ - Dependencies: []*chartutil.Dependency{ - {Name: "redis", Repository: "http://example.com", Version: "1.0.0"}, - }, + req: []*chart.Dependency{ + {Name: "redis", Repository: "http://example.com", Version: "1.0.0"}, }, err: true, }, { name: "constraint not satisfied failure", - req: &chartutil.Requirements{ - Dependencies: []*chartutil.Dependency{ - {Name: "alpine", Repository: "http://example.com", Version: ">=1.0.0"}, - }, + req: []*chart.Dependency{ + {Name: "alpine", Repository: "http://example.com", Version: ">=1.0.0"}, }, err: true, }, { name: "valid lock", - req: &chartutil.Requirements{ - Dependencies: []*chartutil.Dependency{ - {Name: "alpine", Repository: "http://example.com", Version: ">=0.1.0"}, - }, + req: []*chart.Dependency{ + {Name: "alpine", Repository: "http://example.com", Version: ">=0.1.0"}, }, - expect: &chartutil.RequirementsLock{ - Dependencies: []*chartutil.Dependency{ + expect: &chart.Lock{ + Dependencies: []*chart.Dependency{ {Name: "alpine", Repository: "http://example.com", Version: "0.2.0"}, }, }, }, { name: "repo from valid local path", - req: &chartutil.Requirements{ - Dependencies: []*chartutil.Dependency{ - {Name: "signtest", Repository: "file://../../../../cmd/helm/testdata/testcharts/signtest", Version: "0.1.0"}, - }, + req: []*chart.Dependency{ + {Name: "signtest", Repository: "file://../../../../cmd/helm/testdata/testcharts/signtest", Version: "0.1.0"}, }, - expect: &chartutil.RequirementsLock{ - Dependencies: []*chartutil.Dependency{ + expect: &chart.Lock{ + Dependencies: []*chart.Dependency{ {Name: "signtest", Repository: "file://../../../../cmd/helm/testdata/testcharts/signtest", Version: "0.1.0"}, }, }, }, { name: "repo from invalid local path", - req: &chartutil.Requirements{ - Dependencies: []*chartutil.Dependency{ - {Name: "notexist", Repository: "file://../testdata/notexist", Version: "0.1.0"}, - }, + req: []*chart.Dependency{ + {Name: "notexist", Repository: "file://../testdata/notexist", Version: "0.1.0"}, }, err: true, }, @@ -128,7 +114,7 @@ func TestResolve(t *testing.T) { } // Check fields. - if len(l.Dependencies) != len(tt.req.Dependencies) { + if len(l.Dependencies) != len(tt.req) { t.Errorf("%s: wrong number of dependencies in lock", tt.name) } d0 := l.Dependencies[0] @@ -146,11 +132,9 @@ func TestResolve(t *testing.T) { } func TestHashReq(t *testing.T) { - expect := "sha256:e70e41f8922e19558a8bf62f591a8b70c8e4622e3c03e5415f09aba881f13885" - req := &chartutil.Requirements{ - Dependencies: []*chartutil.Dependency{ - {Name: "alpine", Version: "0.1.0", Repository: "http://localhost:8879/charts"}, - }, + expect := "sha256:d661820b01ed7bcf26eed8f01cf16380e0a76326ba33058d3150f919d9b15bc0" + req := []*chart.Dependency{ + {Name: "alpine", Version: "0.1.0", Repository: "http://localhost:8879/charts"}, } h, err := HashReq(req) if err != nil { @@ -160,7 +144,7 @@ func TestHashReq(t *testing.T) { t.Errorf("Expected %q, got %q", expect, h) } - req = &chartutil.Requirements{Dependencies: []*chartutil.Dependency{}} + req = []*chart.Dependency{} h, err = HashReq(req) if err != nil { t.Fatal(err) diff --git a/pkg/storage/driver/cfgmaps.go b/pkg/storage/driver/cfgmaps.go index 4821049f1..d91b71b91 100644 --- a/pkg/storage/driver/cfgmaps.go +++ b/pkg/storage/driver/cfgmaps.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -66,7 +66,7 @@ func (cfgmaps *ConfigMaps) Get(key string) (*rspb.Release, error) { obj, err := cfgmaps.impl.Get(key, metav1.GetOptions{}) if err != nil { if apierrors.IsNotFound(err) { - return nil, ErrReleaseNotFound(key) + return nil, ErrReleaseNotFound } cfgmaps.Log("get: failed to get %q: %s", key, err) @@ -132,7 +132,7 @@ func (cfgmaps *ConfigMaps) Query(labels map[string]string) ([]*rspb.Release, err } if len(list.Items) == 0 { - return nil, ErrReleaseNotFound(labels["name"]) + return nil, ErrReleaseNotFound } var results []*rspb.Release @@ -165,7 +165,7 @@ func (cfgmaps *ConfigMaps) Create(key string, rls *rspb.Release) error { // push the configmap object out into the kubiverse if _, err := cfgmaps.impl.Create(obj); err != nil { if apierrors.IsAlreadyExists(err) { - return ErrReleaseExists(key) + return ErrReleaseExists } cfgmaps.Log("create: failed to create: %s", err) @@ -203,7 +203,7 @@ func (cfgmaps *ConfigMaps) Delete(key string) (rls *rspb.Release, err error) { // fetch the release to check existence if rls, err = cfgmaps.Get(key); err != nil { if apierrors.IsNotFound(err) { - return nil, ErrReleaseExists(rls.Name) + return nil, ErrReleaseExists } cfgmaps.Log("delete: failed to get release %q: %s", key, err) diff --git a/pkg/storage/driver/cfgmaps_test.go b/pkg/storage/driver/cfgmaps_test.go index d92b35764..65c6fc1dd 100644 --- a/pkg/storage/driver/cfgmaps_test.go +++ b/pkg/storage/driver/cfgmaps_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 @@ -85,8 +85,8 @@ func TestUNcompressedConfigMapGet(t *testing.T) { func TestConfigMapList(t *testing.T) { cfgmaps := newTestFixtureCfgMaps(t, []*rspb.Release{ - releaseStub("key-1", 1, "default", rspb.StatusDeleted), - releaseStub("key-2", 1, "default", rspb.StatusDeleted), + releaseStub("key-1", 1, "default", rspb.StatusUninstalled), + releaseStub("key-2", 1, "default", rspb.StatusUninstalled), releaseStub("key-3", 1, "default", rspb.StatusDeployed), releaseStub("key-4", 1, "default", rspb.StatusDeployed), releaseStub("key-5", 1, "default", rspb.StatusSuperseded), @@ -95,7 +95,7 @@ func TestConfigMapList(t *testing.T) { // list all deleted releases del, err := cfgmaps.List(func(rel *rspb.Release) bool { - return rel.Info.Status == rspb.StatusDeleted + return rel.Info.Status == rspb.StatusUninstalled }) // check if err != nil { diff --git a/pkg/storage/driver/driver.go b/pkg/storage/driver/driver.go index e28548ab0..1bd8d2e6d 100644 --- a/pkg/storage/driver/driver.go +++ b/pkg/storage/driver/driver.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -24,11 +24,11 @@ import ( var ( // ErrReleaseNotFound indicates that a release is not found. - ErrReleaseNotFound = func(release string) error { return errors.Errorf("release: %q not found", release) } + ErrReleaseNotFound = errors.New("release: not found") // ErrReleaseExists indicates that a release already exists. - ErrReleaseExists = func(release string) error { return errors.Errorf("release: %q already exists", release) } + ErrReleaseExists = errors.New("release: already exists") // ErrInvalidKey indicates that a release key could not be parsed. - ErrInvalidKey = func(release string) error { return errors.Errorf("release: %q invalid key", release) } + ErrInvalidKey = errors.Errorf("release: invalid key") ) // Creator is the interface that wraps the Create method. diff --git a/pkg/storage/driver/labels.go b/pkg/storage/driver/labels.go index 8668d665b..eb7118fe5 100644 --- a/pkg/storage/driver/labels.go +++ b/pkg/storage/driver/labels.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/storage/driver/labels_test.go b/pkg/storage/driver/labels_test.go index af0bd24e5..e8d7fc90c 100644 --- a/pkg/storage/driver/labels_test.go +++ b/pkg/storage/driver/labels_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/storage/driver/memory.go b/pkg/storage/driver/memory.go index 8bce774e9..df89bfed3 100644 --- a/pkg/storage/driver/memory.go +++ b/pkg/storage/driver/memory.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -53,16 +53,16 @@ func (mem *Memory) Get(key string) (*rspb.Release, error) { case 2: name, ver := elems[0], elems[1] if _, err := strconv.Atoi(ver); err != nil { - return nil, ErrInvalidKey(key) + return nil, ErrInvalidKey } if recs, ok := mem.cache[name]; ok { if r := recs.Get(key); r != nil { return r.rls, nil } } - return nil, ErrReleaseNotFound(key) + return nil, ErrReleaseNotFound default: - return nil, ErrInvalidKey(key) + return nil, ErrInvalidKey } } @@ -131,7 +131,7 @@ func (mem *Memory) Update(key string, rls *rspb.Release) error { rs.Replace(key, newRecord(key, rls)) return nil } - return ErrReleaseNotFound(rls.Name) + return ErrReleaseNotFound } // Delete deletes a release or returns ErrReleaseNotFound. @@ -141,12 +141,12 @@ func (mem *Memory) Delete(key string) (*rspb.Release, error) { elems := strings.Split(key, ".v") if len(elems) != 2 { - return nil, ErrInvalidKey(key) + return nil, ErrInvalidKey } name, ver := elems[0], elems[1] if _, err := strconv.Atoi(ver); err != nil { - return nil, ErrInvalidKey(key) + return nil, ErrInvalidKey } if recs, ok := mem.cache[name]; ok { if r := recs.Remove(key); r != nil { @@ -155,7 +155,7 @@ func (mem *Memory) Delete(key string) (*rspb.Release, error) { return r.rls, nil } } - return nil, ErrReleaseNotFound(key) + return nil, ErrReleaseNotFound } // wlock locks mem for writing diff --git a/pkg/storage/driver/memory_test.go b/pkg/storage/driver/memory_test.go index cfbab5a11..9bab0a74c 100644 --- a/pkg/storage/driver/memory_test.go +++ b/pkg/storage/driver/memory_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -123,7 +123,7 @@ func TestMemoryUpdate(t *testing.T) { { "update release does not exist", "rls-z.v1", - releaseStub("rls-z", 1, "default", rspb.StatusDeleted), + releaseStub("rls-z", 1, "default", rspb.StatusUninstalled), true, }, } diff --git a/pkg/storage/driver/mock_test.go b/pkg/storage/driver/mock_test.go index 8e5996bd3..bdb9236db 100644 --- a/pkg/storage/driver/mock_test.go +++ b/pkg/storage/driver/mock_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/storage/driver/records.go b/pkg/storage/driver/records.go index 5581dfbf1..5bb497e7d 100644 --- a/pkg/storage/driver/records.go +++ b/pkg/storage/driver/records.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -36,7 +36,7 @@ func (rs *records) Add(r *record) error { } if rs.Exists(r.key) { - return ErrReleaseExists(r.key) + return ErrReleaseExists } *rs = append(*rs, r) diff --git a/pkg/storage/driver/records_test.go b/pkg/storage/driver/records_test.go index fc8affee3..141a5c590 100644 --- a/pkg/storage/driver/records_test.go +++ b/pkg/storage/driver/records_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/storage/driver/secrets.go b/pkg/storage/driver/secrets.go index 99057da29..1c9ee96e9 100644 --- a/pkg/storage/driver/secrets.go +++ b/pkg/storage/driver/secrets.go @@ -1,5 +1,5 @@ /* -Copyright 2017 The Kubernetes Authors All rights reserved. +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. @@ -66,7 +66,7 @@ func (secrets *Secrets) Get(key string) (*rspb.Release, error) { obj, err := secrets.impl.Get(key, metav1.GetOptions{}) if err != nil { if apierrors.IsNotFound(err) { - return nil, ErrReleaseNotFound(key) + return nil, ErrReleaseNotFound } return nil, errors.Wrapf(err, "get: failed to get %q", key) } @@ -123,7 +123,7 @@ func (secrets *Secrets) Query(labels map[string]string) ([]*rspb.Release, error) } if len(list.Items) == 0 { - return nil, ErrReleaseNotFound(labels["name"]) + return nil, ErrReleaseNotFound } var results []*rspb.Release @@ -155,7 +155,7 @@ func (secrets *Secrets) Create(key string, rls *rspb.Release) error { // push the secret object out into the kubiverse if _, err := secrets.impl.Create(obj); err != nil { if apierrors.IsAlreadyExists(err) { - return ErrReleaseExists(rls.Name) + return ErrReleaseExists } return errors.Wrap(err, "create: failed to create") @@ -187,7 +187,7 @@ func (secrets *Secrets) Delete(key string) (rls *rspb.Release, err error) { // fetch the release to check existence if rls, err = secrets.Get(key); err != nil { if apierrors.IsNotFound(err) { - return nil, ErrReleaseExists(rls.Name) + return nil, ErrReleaseExists } return nil, errors.Wrapf(err, "delete: failed to get release %q", key) diff --git a/pkg/storage/driver/secrets_test.go b/pkg/storage/driver/secrets_test.go index 6596753a8..d791959d3 100644 --- a/pkg/storage/driver/secrets_test.go +++ b/pkg/storage/driver/secrets_test.go @@ -1,5 +1,5 @@ /* -Copyright 2017 The Kubernetes Authors All rights reserved. +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 @@ -85,8 +85,8 @@ func TestUNcompressedSecretGet(t *testing.T) { func TestSecretList(t *testing.T) { secrets := newTestFixtureSecrets(t, []*rspb.Release{ - releaseStub("key-1", 1, "default", rspb.StatusDeleted), - releaseStub("key-2", 1, "default", rspb.StatusDeleted), + releaseStub("key-1", 1, "default", rspb.StatusUninstalled), + releaseStub("key-2", 1, "default", rspb.StatusUninstalled), releaseStub("key-3", 1, "default", rspb.StatusDeployed), releaseStub("key-4", 1, "default", rspb.StatusDeployed), releaseStub("key-5", 1, "default", rspb.StatusSuperseded), @@ -95,7 +95,7 @@ func TestSecretList(t *testing.T) { // list all deleted releases del, err := secrets.List(func(rel *rspb.Release) bool { - return rel.Info.Status == rspb.StatusDeleted + return rel.Info.Status == rspb.StatusUninstalled }) // check if err != nil { diff --git a/pkg/storage/driver/util.go b/pkg/storage/driver/util.go index 234707c86..a4aba5d9c 100644 --- a/pkg/storage/driver/util.go +++ b/pkg/storage/driver/util.go @@ -1,5 +1,5 @@ /* -Copyright 2017 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index dadc30e8b..0b1cf9279 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -82,12 +82,12 @@ func (s *Storage) ListReleases() ([]*rspb.Release, error) { return s.Driver.List(func(_ *rspb.Release) bool { return true }) } -// ListDeleted returns all releases with Status == DELETED. An error is returned +// ListUninstalled returns all releases with Status == UNINSTALLED. An error is returned // if the storage backend fails to retrieve the releases. -func (s *Storage) ListDeleted() ([]*rspb.Release, error) { - s.Log("listing deleted releases in storage") +func (s *Storage) ListUninstalled() ([]*rspb.Release, error) { + s.Log("listing uninstalled releases in storage") return s.Driver.List(func(rls *rspb.Release) bool { - return relutil.StatusFilter(rspb.StatusDeleted).Check(rls) + return relutil.StatusFilter(rspb.StatusUninstalled).Check(rls) }) } diff --git a/pkg/storage/storage_test.go b/pkg/storage/storage_test.go index b5ba429b4..bf1aae4bb 100644 --- a/pkg/storage/storage_test.go +++ b/pkg/storage/storage_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -61,7 +61,7 @@ func TestStorageUpdate(t *testing.T) { assertErrNil(t.Fatal, storage.Create(rls), "StoreRelease") // modify the release - rls.Info.Status = rspb.StatusDeleted + rls.Info.Status = rspb.StatusUninstalled assertErrNil(t.Fatal, storage.Update(rls), "UpdateRelease") // retrieve the updated release @@ -127,8 +127,8 @@ func TestStorageList(t *testing.T) { rls2 := ReleaseTestData{Name: "relaxed-cat", Status: rspb.StatusSuperseded}.ToRelease() rls3 := ReleaseTestData{Name: "hungry-hippo", Status: rspb.StatusDeployed}.ToRelease() rls4 := ReleaseTestData{Name: "angry-beaver", Status: rspb.StatusDeployed}.ToRelease() - rls5 := ReleaseTestData{Name: "opulent-frog", Status: rspb.StatusDeleted}.ToRelease() - rls6 := ReleaseTestData{Name: "happy-liger", Status: rspb.StatusDeleted}.ToRelease() + rls5 := ReleaseTestData{Name: "opulent-frog", Status: rspb.StatusUninstalled}.ToRelease() + rls6 := ReleaseTestData{Name: "happy-liger", Status: rspb.StatusUninstalled}.ToRelease() // create the release records in the storage assertErrNil(t.Fatal, storage.Create(rls0), "Storing release 'rls0'") @@ -145,9 +145,9 @@ func TestStorageList(t *testing.T) { NumExpected int ListFunc func() ([]*rspb.Release, error) }{ - {"ListDeleted", 2, storage.ListDeleted}, {"ListDeployed", 2, storage.ListDeployed}, {"ListReleases", 7, storage.ListReleases}, + {"ListUninstalled", 2, storage.ListUninstalled}, } setup() diff --git a/pkg/strvals/doc.go b/pkg/strvals/doc.go index d2b859e67..f17290587 100644 --- a/pkg/strvals/doc.go +++ b/pkg/strvals/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/strvals/parser.go b/pkg/strvals/parser.go index 40c001942..610f8ad4a 100644 --- a/pkg/strvals/parser.go +++ b/pkg/strvals/parser.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/strvals/parser_test.go b/pkg/strvals/parser_test.go index fd287bf8a..0a1d5ef58 100644 --- a/pkg/strvals/parser_test.go +++ b/pkg/strvals/parser_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/sympath/walk.go b/pkg/sympath/walk.go index b50756a63..756fc74c3 100644 --- a/pkg/sympath/walk.go +++ b/pkg/sympath/walk.go @@ -1,10 +1,10 @@ /* -Copyright (c) for portions of walk.go are held by The Go Authors, 2009 and are provided under +Copyright The Helm Authors.go are held by The Go Authors, 2009 and are provided under the BSD license. https://github.com/golang/go/blob/master/LICENSE -Copyright 2017 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/sympath/walk_test.go b/pkg/sympath/walk_test.go index d86d8dabd..c7403bcfa 100644 --- a/pkg/sympath/walk_test.go +++ b/pkg/sympath/walk_test.go @@ -1,10 +1,10 @@ /* -Copyright (c) for portions of walk_test.go are held by The Go Authors, 2009 and are provided under +Copyright The Helm Authors.go are held by The Go Authors, 2009 and are provided under the BSD license. https://github.com/golang/go/blob/master/LICENSE -Copyright 2017 The Kubernetes Authors All rights reserved. +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 diff --git a/pkg/tiller/engine.go b/pkg/tiller/engine.go new file mode 100644 index 000000000..d1485fe50 --- /dev/null +++ b/pkg/tiller/engine.go @@ -0,0 +1,40 @@ +/* +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 tiller + +import ( + "k8s.io/helm/pkg/chart" + "k8s.io/helm/pkg/chartutil" +) + +// Engine represents a template engine that can render templates. +// +// For some engines, "rendering" includes both compiling and executing. (Other +// engines do not distinguish between phases.) +// +// The engine returns a map where the key is the named output entity (usually +// a file name) and the value is the rendered content of the template. +// +// An Engine must be capable of executing multiple concurrent requests, but +// without tainting one request's environment with data from another request. +type Engine interface { + // Render renders a chart. + // + // It receives a chart, a config, and a map of overrides to the config. + // Overrides are assumed to be passed from the system, not the user. + Render(*chart.Chart, chartutil.Values) (map[string]string, error) +} diff --git a/pkg/tiller/environment/environment.go b/pkg/tiller/environment/environment.go index 052120ff9..8a166fe23 100644 --- a/pkg/tiller/environment/environment.go +++ b/pkg/tiller/environment/environment.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -26,67 +26,12 @@ import ( "io" "time" - "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/api/core/v1" + "k8s.io/cli-runtime/pkg/genericclioptions/resource" - "k8s.io/helm/pkg/chartutil" - "k8s.io/helm/pkg/engine" - "k8s.io/helm/pkg/hapi/chart" "k8s.io/helm/pkg/kube" ) -// GoTplEngine is the name of the Go template engine, as registered in the EngineYard. -const GoTplEngine = "gotpl" - -// DefaultEngine points to the engine that the EngineYard should treat as the -// default. A chart that does not specify an engine may be run through the -// default engine. -var DefaultEngine = GoTplEngine - -// EngineYard maps engine names to engine implementations. -type EngineYard map[string]Engine - -// Get retrieves a template engine by name. -// -// If no matching template engine is found, the second return value will -// be false. -func (y EngineYard) Get(k string) (Engine, bool) { - e, ok := y[k] - return e, ok -} - -// Default returns the default template engine. -// -// The default is specified by DefaultEngine. -// -// If the default template engine cannot be found, this panics. -func (y EngineYard) Default() Engine { - d, ok := y[DefaultEngine] - if !ok { - // This is a developer error! - panic("Default template engine does not exist") - } - return d -} - -// Engine represents a template engine that can render templates. -// -// For some engines, "rendering" includes both compiling and executing. (Other -// engines do not distinguish between phases.) -// -// The engine returns a map where the key is the named output entity (usually -// a file name) and the value is the rendered content of the template. -// -// An Engine must be capable of executing multiple concurrent requests, but -// without tainting one request's environment with data from another request. -type Engine interface { - // Render renders a chart. - // - // It receives a chart, a config, and a map of overrides to the config. - // Overrides are assumed to be passed from the system, not the user. - Render(*chart.Chart, chartutil.Values) (map[string]string, error) -} - // KubeClient represents a client capable of communicating with the Kubernetes API. // // A KubeClient must be concurrency safe. @@ -137,7 +82,7 @@ type KubeClient interface { // WaitAndGetCompletedPodPhase waits up to a timeout until a pod enters a completed phase // and returns said phase (PodSucceeded or PodFailed qualify). - WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (core.PodPhase, error) + WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (v1.PodPhase, error) } // PrintingKubeClient implements KubeClient, but simply prints the reader to @@ -189,29 +134,7 @@ func (p *PrintingKubeClient) BuildUnstructured(ns string, reader io.Reader) (kub } // WaitAndGetCompletedPodPhase implements KubeClient WaitAndGetCompletedPodPhase. -func (p *PrintingKubeClient) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (core.PodPhase, error) { +func (p *PrintingKubeClient) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (v1.PodPhase, error) { _, err := io.Copy(p.Out, reader) - return core.PodUnknown, err -} - -// Environment provides the context for executing a client request. -// -// All services in a context are concurrency safe. -type Environment struct { - // EngineYard provides access to the known template engines. - EngineYard EngineYard -} - -// New returns an environment initialized with the defaults. -func New() *Environment { - e := engine.New() - var ey EngineYard = map[string]Engine{ - // Currently, the only template engine we support is the GoTpl one. But - // we can easily add some here. - GoTplEngine: e, - } - - return &Environment{ - EngineYard: ey, - } + return v1.PodUnknown, err } diff --git a/pkg/tiller/environment/environment_test.go b/pkg/tiller/environment/environment_test.go index 1f06b1f28..fe458479f 100644 --- a/pkg/tiller/environment/environment_test.go +++ b/pkg/tiller/environment/environment_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -22,22 +22,12 @@ import ( "testing" "time" - "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/api/core/v1" + "k8s.io/cli-runtime/pkg/genericclioptions/resource" - "k8s.io/helm/pkg/chartutil" - "k8s.io/helm/pkg/hapi/chart" "k8s.io/helm/pkg/kube" ) -type mockEngine struct { - out map[string]string -} - -func (e *mockEngine) Render(chrt *chart.Chart, v chartutil.Values) (map[string]string, error) { - return e.out, nil -} - type mockKubeClient struct{} func (k *mockKubeClient) Create(ns string, r io.Reader, timeout int64, shouldWait bool) error { @@ -61,33 +51,17 @@ func (k *mockKubeClient) Build(ns string, reader io.Reader) (kube.Result, error) func (k *mockKubeClient) BuildUnstructured(ns string, reader io.Reader) (kube.Result, error) { return []*resource.Info{}, nil } -func (k *mockKubeClient) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (core.PodPhase, error) { - return core.PodUnknown, nil +func (k *mockKubeClient) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (v1.PodPhase, error) { + return v1.PodUnknown, nil } -func (k *mockKubeClient) WaitAndGetCompletedPodStatus(namespace string, reader io.Reader, timeout time.Duration) (core.PodPhase, error) { +func (k *mockKubeClient) WaitAndGetCompletedPodStatus(namespace string, reader io.Reader, timeout time.Duration) (v1.PodPhase, error) { return "", nil } -var _ Engine = &mockEngine{} var _ KubeClient = &mockKubeClient{} var _ KubeClient = &PrintingKubeClient{} -func TestEngine(t *testing.T) { - eng := &mockEngine{out: map[string]string{"albatross": "test"}} - - env := New() - env.EngineYard = EngineYard(map[string]Engine{"test": eng}) - - if engine, ok := env.EngineYard.Get("test"); !ok { - t.Errorf("failed to get engine from EngineYard") - } else if out, err := engine.Render(&chart.Chart{}, map[string]interface{}{}); err != nil { - t.Errorf("unexpected template error: %s", err) - } else if out["albatross"] != "test" { - t.Errorf("expected 'test', got %q", out["albatross"]) - } -} - func TestKubeClient(t *testing.T) { kc := &mockKubeClient{} diff --git a/pkg/tiller/hook_sorter.go b/pkg/tiller/hook_sorter.go index cc6e7e992..35bff9bed 100644 --- a/pkg/tiller/hook_sorter.go +++ b/pkg/tiller/hook_sorter.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -17,37 +17,16 @@ limitations under the License. package tiller import ( - "sort" - "k8s.io/helm/pkg/hapi/release" ) -// sortByHookWeight does an in-place sort of hooks by their supplied weight. -func sortByHookWeight(hooks []*release.Hook) []*release.Hook { - hs := newHookWeightSorter(hooks) - sort.Sort(hs) - return hs.hooks -} - -type hookWeightSorter struct { - hooks []*release.Hook -} - -func newHookWeightSorter(h []*release.Hook) *hookWeightSorter { - return &hookWeightSorter{ - hooks: h, - } -} - -func (hs *hookWeightSorter) Len() int { return len(hs.hooks) } - -func (hs *hookWeightSorter) Swap(i, j int) { - hs.hooks[i], hs.hooks[j] = hs.hooks[j], hs.hooks[i] -} +type hookByWeight []*release.Hook -func (hs *hookWeightSorter) Less(i, j int) bool { - if hs.hooks[i].Weight == hs.hooks[j].Weight { - return hs.hooks[i].Name < hs.hooks[j].Name +func (x hookByWeight) Len() int { return len(x) } +func (x hookByWeight) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x hookByWeight) Less(i, j int) bool { + if x[i].Weight == x[j].Weight { + return x[i].Name < x[j].Name } - return hs.hooks[i].Weight < hs.hooks[j].Weight + return x[i].Weight < x[j].Weight } diff --git a/pkg/tiller/hook_sorter_test.go b/pkg/tiller/hook_sorter_test.go index 4e33bdad4..c46c44b57 100644 --- a/pkg/tiller/hook_sorter_test.go +++ b/pkg/tiller/hook_sorter_test.go @@ -1,5 +1,5 @@ /* -Copyright 2017 The Kubernetes Authors All rights reserved. +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. @@ -17,6 +17,7 @@ limitations under the License. package tiller import ( + "sort" "testing" "k8s.io/helm/pkg/hapi/release" @@ -61,10 +62,10 @@ func TestHookSorter(t *testing.T) { }, } - res := sortByHookWeight(hooks) + sort.Sort(hookByWeight(hooks)) got := "" expect := "abcdefg" - for _, r := range res { + for _, r := range hooks { got += r.Name } if got != expect { diff --git a/pkg/tiller/hooks.go b/pkg/tiller/hooks.go index 29c311509..5a85036e3 100644 --- a/pkg/tiller/hooks.go +++ b/pkg/tiller/hooks.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -78,7 +78,7 @@ type manifestFile struct { // // Files that do not parse into the expected format are simply placed into a map and // returned. -func sortManifests(files map[string]string, apis chartutil.VersionSet, sort SortOrder) ([]*release.Hook, []Manifest, error) { +func SortManifests(files map[string]string, apis chartutil.VersionSet, sort SortOrder) ([]*release.Hook, []Manifest, error) { result := &result{} for filePath, c := range files { diff --git a/pkg/tiller/hooks_test.go b/pkg/tiller/hooks_test.go index 694c1cab1..abd2adf63 100644 --- a/pkg/tiller/hooks_test.go +++ b/pkg/tiller/hooks_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -140,7 +140,7 @@ metadata: manifests[o.path] = o.manifest } - hs, generic, err := sortManifests(manifests, chartutil.NewVersionSet("v1", "v1beta1"), InstallOrder) + hs, generic, err := SortManifests(manifests, chartutil.NewVersionSet("v1", "v1beta1"), InstallOrder) if err != nil { t.Fatalf("Unexpected error: %s", err) } diff --git a/pkg/tiller/kind_sorter.go b/pkg/tiller/kind_sorter.go index f367e65c8..d0e4d0912 100644 --- a/pkg/tiller/kind_sorter.go +++ b/pkg/tiller/kind_sorter.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/tiller/kind_sorter_test.go b/pkg/tiller/kind_sorter_test.go index ef7296e89..ef7d3c1b7 100644 --- a/pkg/tiller/kind_sorter_test.go +++ b/pkg/tiller/kind_sorter_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/tiller/release_content.go b/pkg/tiller/release_content.go index 0ec04df68..a97a7c3a3 100644 --- a/pkg/tiller/release_content.go +++ b/pkg/tiller/release_content.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/tiller/release_content_test.go b/pkg/tiller/release_content_test.go index b5947fc57..b2ec33edc 100644 --- a/pkg/tiller/release_content_test.go +++ b/pkg/tiller/release_content_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -34,7 +34,7 @@ func TestGetReleaseContent(t *testing.T) { t.Errorf("Error getting release content: %s", err) } - if res.Chart.Metadata.Name != rel.Chart.Metadata.Name { - t.Errorf("Expected %q, got %q", rel.Chart.Metadata.Name, res.Chart.Metadata.Name) + if res.Chart.Name() != rel.Chart.Name() { + t.Errorf("Expected %q, got %q", rel.Chart.Name(), res.Chart.Name()) } } diff --git a/pkg/tiller/release_history.go b/pkg/tiller/release_history.go index fc9889a8a..1b69d9f0e 100644 --- a/pkg/tiller/release_history.go +++ b/pkg/tiller/release_history.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/tiller/release_history_test.go b/pkg/tiller/release_history_test.go index 37d21263a..65ae8d69c 100644 --- a/pkg/tiller/release_history_test.go +++ b/pkg/tiller/release_history_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/tiller/release_install.go b/pkg/tiller/release_install.go index db07249f5..f91c8e555 100644 --- a/pkg/tiller/release_install.go +++ b/pkg/tiller/release_install.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -66,16 +66,16 @@ func (s *ReleaseServer) prepareRelease(req *hapi.InstallReleaseRequest) (*releas } revision := 1 - ts := time.Now() options := chartutil.ReleaseOptions{ Name: name, IsInstall: true, } - valuesToRender, err := chartutil.ToRenderValuesCaps(req.Chart, req.Values, options, caps) + valuesToRender, err := chartutil.ToRenderValues(req.Chart, req.Values, options, caps) if err != nil { return nil, err } + ts := time.Now() hooks, manifestDoc, notesTxt, err := s.renderResources(req.Chart, valuesToRender, caps.APIVersions) if err != nil { // Return a release with partial data so that client can show debugging diff --git a/pkg/tiller/release_install_test.go b/pkg/tiller/release_install_test.go index 3f067ac47..47503f93d 100644 --- a/pkg/tiller/release_install_test.go +++ b/pkg/tiller/release_install_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -28,12 +28,12 @@ import ( func TestInstallRelease(t *testing.T) { rs := rsFixture(t) - req := installRequest() + req := installRequest(withName("test-install-release")) res, err := rs.InstallRelease(req) if err != nil { t.Fatalf("Failed install: %s", err) } - if res.Name == "" { + if res.Name != "test-install-release" { t.Errorf("Expected release name.") } if res.Namespace != "spaced" { @@ -78,11 +78,26 @@ func TestInstallRelease(t *testing.T) { } } +func TestInstallRelease_NoName(t *testing.T) { + rs := rsFixture(t) + + // No name supplied here, should cause failure. + req := installRequest() + _, err := rs.InstallRelease(req) + if err == nil { + t.Fatal("expected failure when no name is specified") + } + if !strings.Contains(err.Error(), "name is required") { + t.Errorf("Expected message %q to include 'name is required'", err.Error()) + } +} + func TestInstallRelease_WithNotes(t *testing.T) { rs := rsFixture(t) req := installRequest( withChart(withNotes(notesText)), + withName("with-notes"), ) res, err := rs.InstallRelease(req) if err != nil { @@ -141,7 +156,8 @@ func TestInstallRelease_WithNotesRendered(t *testing.T) { rs := rsFixture(t) req := installRequest( - withChart(withNotes(notesText + " {{.Release.Name}}")), + withChart(withNotes(notesText+" {{.Release.Name}}")), + withName("with-notes"), ) res, err := rs.InstallRelease(req) if err != nil { @@ -203,7 +219,7 @@ func TestInstallRelease_WithChartAndDependencyNotes(t *testing.T) { req := installRequest(withChart( withNotes(notesText), withDependency(withNotes(notesText+" child")), - )) + ), withName("with-chart-and-dependency-notes")) res, err := rs.InstallRelease(req) if err != nil { t.Fatalf("Failed install: %s", err) @@ -233,13 +249,14 @@ func TestInstallRelease_DryRun(t *testing.T) { req := installRequest(withDryRun(), withChart(withSampleTemplates()), + withName("test-dry-run"), ) res, err := rs.InstallRelease(req) if err != nil { t.Errorf("Failed install: %s", err) } - if res.Name == "" { - t.Errorf("Expected release name.") + if res.Name != "test-dry-run" { + t.Errorf("unexpected release name: %q", res.Name) } if !strings.Contains(res.Manifest, "---\n# Source: hello/templates/hello\nhello: world") { @@ -283,7 +300,7 @@ func TestInstallRelease_NoHooks(t *testing.T) { rs := rsFixture(t) rs.Releases.Create(releaseStub()) - req := installRequest(withDisabledHooks()) + req := installRequest(withDisabledHooks(), withName("no-hooks")) res, err := rs.InstallRelease(req) if err != nil { t.Errorf("Failed install: %s", err) @@ -299,7 +316,7 @@ func TestInstallRelease_FailedHooks(t *testing.T) { rs.Releases.Create(releaseStub()) rs.KubeClient = newHookFailingKubeClient() - req := installRequest() + req := installRequest(withName("failed-hooks")) res, err := rs.InstallRelease(req) if err == nil { t.Error("Expected failed install") @@ -314,7 +331,7 @@ func TestInstallRelease_ReuseName(t *testing.T) { rs := rsFixture(t) rs.Log = t.Logf rel := releaseStub() - rel.Info.Status = release.StatusDeleted + rel.Info.Status = release.StatusUninstalled rs.Releases.Create(rel) req := installRequest( @@ -345,6 +362,7 @@ func TestInstallRelease_KubeVersion(t *testing.T) { req := installRequest( withChart(withKube(">=0.0.0")), + withName("kube-version"), ) _, err := rs.InstallRelease(req) if err != nil { @@ -357,6 +375,7 @@ func TestInstallRelease_WrongKubeVersion(t *testing.T) { req := installRequest( withChart(withKube(">=5.0.0")), + withName("wrong-kube-version"), ) _, err := rs.InstallRelease(req) diff --git a/pkg/tiller/release_list.go b/pkg/tiller/release_list.go index b232935bf..222369f31 100644 --- a/pkg/tiller/release_list.go +++ b/pkg/tiller/release_list.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/tiller/release_list_test.go b/pkg/tiller/release_list_test.go index c31da8905..abce1f569 100644 --- a/pkg/tiller/release_list_test.go +++ b/pkg/tiller/release_list_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -49,7 +49,7 @@ func TestListReleasesByStatus(t *testing.T) { rs := rsFixture(t) stubs := []*release.Release{ namedReleaseStub("kamal", release.StatusDeployed), - namedReleaseStub("astrolabe", release.StatusDeleted), + namedReleaseStub("astrolabe", release.StatusUninstalled), namedReleaseStub("octant", release.StatusFailed), namedReleaseStub("sextant", release.StatusUnknown), } @@ -69,7 +69,7 @@ func TestListReleasesByStatus(t *testing.T) { }, { names: []string{"astrolabe"}, - statusCodes: []release.ReleaseStatus{release.StatusDeleted}, + statusCodes: []release.ReleaseStatus{release.StatusUninstalled}, }, { names: []string{"kamal", "octant"}, @@ -79,7 +79,7 @@ func TestListReleasesByStatus(t *testing.T) { names: []string{"kamal", "astrolabe", "octant", "sextant"}, statusCodes: []release.ReleaseStatus{ release.StatusDeployed, - release.StatusDeleted, + release.StatusUninstalled, release.StatusFailed, release.StatusUnknown, }, diff --git a/pkg/tiller/release_rollback.go b/pkg/tiller/release_rollback.go index a2dab07c1..2387062ef 100644 --- a/pkg/tiller/release_rollback.go +++ b/pkg/tiller/release_rollback.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/tiller/release_rollback_test.go b/pkg/tiller/release_rollback_test.go index b6a7cb7a3..295c372d7 100644 --- a/pkg/tiller/release_rollback_test.go +++ b/pkg/tiller/release_rollback_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/tiller/release_server.go b/pkg/tiller/release_server.go index 44c6d7bf2..5652e5789 100644 --- a/pkg/tiller/release_server.go +++ b/pkg/tiller/release_server.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -20,18 +20,19 @@ import ( "bytes" "path" "regexp" + "sort" "strings" "time" "github.com/pkg/errors" - "github.com/technosophos/moniker" "gopkg.in/yaml.v2" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/discovery" + "k8s.io/helm/pkg/chart" "k8s.io/helm/pkg/chartutil" + "k8s.io/helm/pkg/engine" "k8s.io/helm/pkg/hapi" - "k8s.io/helm/pkg/hapi/chart" "k8s.io/helm/pkg/hapi/release" "k8s.io/helm/pkg/hooks" relutil "k8s.io/helm/pkg/releaseutil" @@ -78,7 +79,7 @@ var ValidName = regexp.MustCompile("^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])+ // ReleaseServer implements the server-side gRPC endpoint for the HAPI services. type ReleaseServer struct { - env *environment.Environment + engine Engine discovery discovery.DiscoveryInterface // Releases stores records of releases. @@ -90,9 +91,9 @@ type ReleaseServer struct { } // NewReleaseServer creates a new release server. -func NewReleaseServer(env *environment.Environment, discovery discovery.DiscoveryInterface, kubeClient environment.KubeClient) *ReleaseServer { +func NewReleaseServer(discovery discovery.DiscoveryInterface, kubeClient environment.KubeClient) *ReleaseServer { return &ReleaseServer{ - env: env, + engine: engine.New(), discovery: discovery, Releases: storage.Init(driver.NewMemory()), KubeClient: kubeClient, @@ -124,15 +125,12 @@ func (s *ReleaseServer) reuseValues(req *hapi.UpdateReleaseRequest, current *rel if err != nil { return errors.Wrap(err, "failed to rebuild old values") } - nv, err := yaml.Marshal(oldVals) - if err != nil { - return err - } // merge new values with current b := append(current.Config, '\n') req.Values = append(b, req.Values...) - req.Chart.Values = nv + + req.Chart.Values = oldVals // yaml unmarshal and marshal to remove duplicate keys y := map[string]interface{}{} @@ -161,59 +159,31 @@ func (s *ReleaseServer) reuseValues(req *hapi.UpdateReleaseRequest, current *rel func (s *ReleaseServer) uniqName(start string, reuse bool) (string, error) { - // If a name is supplied, we check to see if that name is taken. If not, it - // is granted. If reuse is true and a deleted release with that name exists, - // we re-grant it. Otherwise, an error is returned. - if start != "" { - - if len(start) > releaseNameMaxLen { - return "", errors.Errorf("release name %q exceeds max length of %d", start, releaseNameMaxLen) - } - - h, err := s.Releases.History(start) - if err != nil || len(h) < 1 { - return start, nil - } - relutil.Reverse(h, relutil.SortByRevision) - rel := h[0] - - if st := rel.Info.Status; reuse && (st == release.StatusDeleted || st == release.StatusFailed) { - // Allowe re-use of names if the previous release is marked deleted. - s.Log("name %s exists but is not in use, reusing name", start) - return start, nil - } else if reuse { - return "", errors.New("cannot re-use a name that is still in use") - } - - return "", errors.Errorf("a release named %s already exists.\nRun: helm ls --all %s; to check the status of the release\nOr run: helm del --purge %s; to delete it", start, start, start) + if start == "" { + return "", errors.New("name is required") } - maxTries := 5 - for i := 0; i < maxTries; i++ { - namer := moniker.New() - name := namer.NameSep("-") - if len(name) > releaseNameMaxLen { - name = name[:releaseNameMaxLen] - } - if _, err := s.Releases.Get(name, 1); strings.Contains(err.Error(), "not found") { - return name, nil - } - s.Log("info: generated name %s is taken. Searching again.", name) + if len(start) > releaseNameMaxLen { + return "", errors.Errorf("release name %q exceeds max length of %d", start, releaseNameMaxLen) } - s.Log("warning: No available release names found after %d tries", maxTries) - return "ERROR", errors.New("no available release name found") -} -func (s *ReleaseServer) engine(ch *chart.Chart) environment.Engine { - renderer := s.env.EngineYard.Default() - if ch.Metadata.Engine != "" { - if r, ok := s.env.EngineYard.Get(ch.Metadata.Engine); ok { - renderer = r - } else { - s.Log("warning: %s requested non-existent template engine %s", ch.Metadata.Name, ch.Metadata.Engine) - } + h, err := s.Releases.History(start) + if err != nil || len(h) < 1 { + return start, nil + } + relutil.Reverse(h, relutil.SortByRevision) + rel := h[0] + + if st := rel.Info.Status; reuse && (st == release.StatusUninstalled || st == release.StatusFailed) { + // Allowe re-use of names if the previous release is marked deleted. + s.Log("name %s exists but is not in use, reusing name", start) + return start, nil + } else if reuse { + return "", errors.New("cannot re-use a name that is still in use") } - return renderer + + return "", errors.Errorf("a release named %s already exists.\nRun: helm ls --all %s; to check the status of the release\nOr run: helm del --purge %s; to delete it", start, start, start) + } // capabilities builds a Capabilities from discovery information. @@ -269,9 +239,8 @@ func (s *ReleaseServer) renderResources(ch *chart.Chart, values chartutil.Values } } - s.Log("rendering %s chart using values", ch.Metadata.Name) - renderer := s.engine(ch) - files, err := renderer.Render(ch, values) + s.Log("rendering %s chart using values", ch.Name()) + files, err := s.engine.Render(ch, values) if err != nil { return nil, nil, "", err } @@ -286,7 +255,7 @@ func (s *ReleaseServer) renderResources(ch *chart.Chart, values chartutil.Values if strings.HasSuffix(k, notesFileSuffix) { // Only apply the notes if it belongs to the parent chart // Note: Do not use filePath.Join since it creates a path with \ which is not expected - if k == path.Join(ch.Metadata.Name, "templates", notesFileSuffix) { + if k == path.Join(ch.Name(), "templates", notesFileSuffix) { notes = v } delete(files, k) @@ -296,7 +265,7 @@ func (s *ReleaseServer) renderResources(ch *chart.Chart, values chartutil.Values // Sort hooks, manifests, and partials. Only hooks and manifests are returned, // as partials are not used after renderer.Render. Empty manifests are also // removed here. - hooks, manifests, err := sortManifests(files, vs, InstallOrder) + hooks, manifests, err := SortManifests(files, vs, InstallOrder) if err != nil { // By catching parse errors here, we can prevent bogus releases from going // to Kubernetes. @@ -351,7 +320,7 @@ func (s *ReleaseServer) execHook(hs []*release.Hook, name, namespace, hook strin } } - executingHooks = sortByHookWeight(executingHooks) + sort.Sort(hookByWeight(executingHooks)) for _, h := range executingHooks { if err := s.deleteHookIfShouldBeDeletedByDeletePolicy(h, hooks.BeforeHookCreation, name, namespace, hook, s.KubeClient); err != nil { diff --git a/pkg/tiller/release_server_test.go b/pkg/tiller/release_server_test.go index de2d63931..cec06a35c 100644 --- a/pkg/tiller/release_server_test.go +++ b/pkg/tiller/release_server_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -28,12 +28,13 @@ import ( "github.com/ghodss/yaml" "github.com/pkg/errors" + "k8s.io/api/core/v1" + "k8s.io/cli-runtime/pkg/genericclioptions/resource" "k8s.io/client-go/kubernetes/fake" - "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/helm/pkg/chart" + "k8s.io/helm/pkg/engine" "k8s.io/helm/pkg/hapi" - "k8s.io/helm/pkg/hapi/chart" "k8s.io/helm/pkg/hapi/release" "k8s.io/helm/pkg/hooks" "k8s.io/helm/pkg/kube" @@ -93,10 +94,9 @@ data: func rsFixture(t *testing.T) *ReleaseServer { t.Helper() - env := environment.New() dc := fake.NewSimpleClientset().Discovery() kc := &environment.PrintingKubeClient{Out: ioutil.Discard} - rs := NewReleaseServer(env, dc, kc) + rs := NewReleaseServer(dc, kc) rs.Log = func(format string, v ...interface{}) { t.Helper() if *verbose { @@ -142,7 +142,7 @@ func withKube(version string) chartOption { func withDependency(dependencyOpts ...chartOption) chartOption { return func(opts *chartOptions) { - opts.Dependencies = append(opts.Dependencies, buildChart(dependencyOpts...)) + opts.AddDependency(buildChart(dependencyOpts...)) } } @@ -323,7 +323,7 @@ func TestUniqName(t *testing.T) { rel1 := releaseStub() rel2 := releaseStub() rel2.Name = "happy-panda" - rel2.Info.Status = release.StatusDeleted + rel2.Info.Status = release.StatusUninstalled rs.Releases.Create(rel1) rs.Releases.Create(rel2) @@ -334,8 +334,8 @@ func TestUniqName(t *testing.T) { reuse bool err bool }{ + {"", "", false, true}, // Blank name is illegal {"first", "first", false, false}, - {"", "[a-z]+-[a-z]+", false, false}, {"angry-panda", "", false, true}, {"happy-panda", "", false, true}, {"happy-panda", "happy-panda", true, false}, @@ -483,26 +483,23 @@ func (kc *mockHooksKubeClient) WatchUntilReady(ns string, r io.Reader, timeout i return nil } -func (kc *mockHooksKubeClient) Update(ns string, currentReader, modifiedReader io.Reader, force, recreate bool, timeout int64, shouldWait bool) error { +func (kc *mockHooksKubeClient) Update(_ string, _, _ io.Reader, _, _ bool, _ int64, _ bool) error { return nil } -func (kc *mockHooksKubeClient) Build(ns string, reader io.Reader) (kube.Result, error) { +func (kc *mockHooksKubeClient) Build(_ string, _ io.Reader) (kube.Result, error) { return []*resource.Info{}, nil } -func (kc *mockHooksKubeClient) BuildUnstructured(ns string, reader io.Reader) (kube.Result, error) { +func (kc *mockHooksKubeClient) BuildUnstructured(_ string, _ io.Reader) (kube.Result, error) { return []*resource.Info{}, nil } -func (kc *mockHooksKubeClient) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (core.PodPhase, error) { - return core.PodUnknown, nil +func (kc *mockHooksKubeClient) WaitAndGetCompletedPodPhase(_ string, _ io.Reader, _ time.Duration) (v1.PodPhase, error) { + return v1.PodUnknown, nil } func deletePolicyStub(kubeClient *mockHooksKubeClient) *ReleaseServer { - e := environment.New() - - dc := fake.NewSimpleClientset().Discovery() return &ReleaseServer{ - env: e, - discovery: dc, + engine: engine.New(), + discovery: fake.NewSimpleClientset().Discovery(), KubeClient: kubeClient, Log: func(_ string, _ ...interface{}) {}, } diff --git a/pkg/tiller/release_status.go b/pkg/tiller/release_status.go index a2b5b1eca..9d3390c8d 100644 --- a/pkg/tiller/release_status.go +++ b/pkg/tiller/release_status.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -63,7 +63,7 @@ func (s *ReleaseServer) GetReleaseStatus(req *hapi.GetReleaseStatusRequest) (*ha // Ok, we got the status of the release as we had jotted down, now we need to match the // manifest we stashed away with reality from the cluster. resp, err := s.KubeClient.Get(rel.Namespace, bytes.NewBufferString(rel.Manifest)) - if sc == release.StatusDeleted || sc == release.StatusFailed { + if sc == release.StatusUninstalled || sc == release.StatusFailed { // Skip errors if this is already deleted or failed. return statusResp, nil } else if err != nil { diff --git a/pkg/tiller/release_status_test.go b/pkg/tiller/release_status_test.go index 9a578c2c6..4a2adc78a 100644 --- a/pkg/tiller/release_status_test.go +++ b/pkg/tiller/release_status_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -43,10 +43,10 @@ func TestGetReleaseStatus(t *testing.T) { } } -func TestGetReleaseStatusDeleted(t *testing.T) { +func TestGetReleaseStatusUninstalled(t *testing.T) { rs := rsFixture(t) rel := releaseStub() - rel.Info.Status = release.StatusDeleted + rel.Info.Status = release.StatusUninstalled if err := rs.Releases.Create(rel); err != nil { t.Fatalf("Could not store mock release: %s", err) } @@ -56,7 +56,7 @@ func TestGetReleaseStatusDeleted(t *testing.T) { t.Fatalf("Error getting release content: %s", err) } - if res.Info.Status != release.StatusDeleted { - t.Errorf("Expected %s, got %s", release.StatusDeleted, res.Info.Status) + if res.Info.Status != release.StatusUninstalled { + t.Errorf("Expected %s, got %s", release.StatusUninstalled, res.Info.Status) } } diff --git a/pkg/tiller/release_testing.go b/pkg/tiller/release_testing.go index 9b277f2ba..0f7ffbeb3 100644 --- a/pkg/tiller/release_testing.go +++ b/pkg/tiller/release_testing.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/tiller/release_testing_test.go b/pkg/tiller/release_testing_test.go index 69756e0ab..06ca530b1 100644 --- a/pkg/tiller/release_testing_test.go +++ b/pkg/tiller/release_testing_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/tiller/release_uninstall.go b/pkg/tiller/release_uninstall.go index c3160a2ad..568741738 100644 --- a/pkg/tiller/release_uninstall.go +++ b/pkg/tiller/release_uninstall.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -30,7 +30,7 @@ import ( relutil "k8s.io/helm/pkg/releaseutil" ) -// UninstallRelease deletes all of the resources associated with this release, and marks the release DELETED. +// UninstallRelease deletes all of the resources associated with this release, and marks the release UNINSTALLED. func (s *ReleaseServer) UninstallRelease(req *hapi.UninstallReleaseRequest) (*hapi.UninstallReleaseResponse, error) { if err := validateReleaseName(req.Name); err != nil { return nil, errors.Errorf("uninstall: Release name is invalid: %s", req.Name) @@ -49,7 +49,7 @@ func (s *ReleaseServer) UninstallRelease(req *hapi.UninstallReleaseRequest) (*ha // TODO: Are there any cases where we want to force a delete even if it's // already marked deleted? - if rel.Info.Status == release.StatusDeleted { + if rel.Info.Status == release.StatusUninstalled { if req.Purge { if err := s.purgeReleases(rels...); err != nil { return nil, errors.Wrap(err, "uninstall: Failed to purge the release") @@ -60,7 +60,7 @@ func (s *ReleaseServer) UninstallRelease(req *hapi.UninstallReleaseRequest) (*ha } s.Log("uninstall: Deleting %s", req.Name) - rel.Info.Status = release.StatusDeleting + rel.Info.Status = release.StatusUninstalling rel.Info.Deleted = time.Now() rel.Info.Description = "Deletion in progress (or silently failed)" res := &hapi.UninstallReleaseResponse{Release: rel} @@ -73,7 +73,7 @@ func (s *ReleaseServer) UninstallRelease(req *hapi.UninstallReleaseRequest) (*ha s.Log("delete hooks disabled for %s", req.Name) } - // From here on out, the release is currently considered to be in StatusDeleting + // From here on out, the release is currently considered to be in StatusUninstalling // state. if err := s.Releases.Update(rel); err != nil { s.Log("uninstall: Failed to store updated release: %s", err) @@ -88,8 +88,8 @@ func (s *ReleaseServer) UninstallRelease(req *hapi.UninstallReleaseRequest) (*ha } } - rel.Info.Status = release.StatusDeleted - rel.Info.Description = "Deletion complete" + rel.Info.Status = release.StatusUninstalled + rel.Info.Description = "Uninstallation complete" if req.Purge { s.Log("purge requested for %s", req.Name) @@ -102,7 +102,7 @@ func (s *ReleaseServer) UninstallRelease(req *hapi.UninstallReleaseRequest) (*ha } if len(errs) > 0 { - return res, errors.Errorf("deletion completed with %d error(s): %s", len(errs), joinErrors(errs)) + return res, errors.Errorf("uninstallation completed with %d error(s): %s", len(errs), joinErrors(errs)) } return res, nil } @@ -132,7 +132,7 @@ func (s *ReleaseServer) deleteRelease(rel *release.Release) (kept string, errs [ } manifests := relutil.SplitManifests(rel.Manifest) - _, files, err := sortManifests(manifests, vs, UninstallOrder) + _, files, err := SortManifests(manifests, vs, UninstallOrder) if err != nil { // We could instead just delete everything in no particular order. // FIXME: One way to delete at this point would be to try a label-based diff --git a/pkg/tiller/release_uninstall_test.go b/pkg/tiller/release_uninstall_test.go index bc2da7110..666134fad 100644 --- a/pkg/tiller/release_uninstall_test.go +++ b/pkg/tiller/release_uninstall_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -41,8 +41,8 @@ func TestUninstallRelease(t *testing.T) { t.Errorf("Expected angry-panda, got %q", res.Release.Name) } - if res.Release.Info.Status != release.StatusDeleted { - t.Errorf("Expected status code to be DELETED, got %s", res.Release.Info.Status) + if res.Release.Info.Status != release.StatusUninstalled { + t.Errorf("Expected status code to be UNINSTALLED, got %s", res.Release.Info.Status) } if res.Release.Hooks[0].LastRun.IsZero() { @@ -53,8 +53,8 @@ func TestUninstallRelease(t *testing.T) { t.Errorf("Expected valid UNIX date, got %d", res.Release.Info.Deleted.Second()) } - if res.Release.Info.Description != "Deletion complete" { - t.Errorf("Expected Deletion complete, got %q", res.Release.Info.Description) + if res.Release.Info.Description != "Uninstallation complete" { + t.Errorf("Expected Uninstallation complete, got %q", res.Release.Info.Description) } } @@ -80,8 +80,8 @@ func TestUninstallPurgeRelease(t *testing.T) { t.Errorf("Expected angry-panda, got %q", res.Release.Name) } - if res.Release.Info.Status != release.StatusDeleted { - t.Errorf("Expected status code to be DELETED, got %s", res.Release.Info.Status) + if res.Release.Info.Status != release.StatusUninstalled { + t.Errorf("Expected status code to be UNINSTALLED, got %s", res.Release.Info.Status) } if res.Release.Info.Deleted.Second() <= 0 { @@ -138,8 +138,8 @@ func TestUninstallReleaseWithKeepPolicy(t *testing.T) { t.Errorf("Expected angry-bunny, got %q", res.Release.Name) } - if res.Release.Info.Status != release.StatusDeleted { - t.Errorf("Expected status code to be DELETED, got %s", res.Release.Info.Status) + if res.Release.Info.Status != release.StatusUninstalled { + t.Errorf("Expected status code to be UNINSTALLED, got %s", res.Release.Info.Status) } if res.Info == "" { diff --git a/pkg/tiller/release_update.go b/pkg/tiller/release_update.go index 3bcc9cfcf..916ac64e7 100644 --- a/pkg/tiller/release_update.go +++ b/pkg/tiller/release_update.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -105,7 +105,7 @@ func (s *ReleaseServer) prepareUpdate(req *hapi.UpdateReleaseRequest) (*release. if err != nil { return nil, nil, err } - valuesToRender, err := chartutil.ToRenderValuesCaps(req.Chart, req.Values, options, caps) + valuesToRender, err := chartutil.ToRenderValues(req.Chart, req.Values, options, caps) if err != nil { return nil, nil, err } @@ -139,7 +139,7 @@ func (s *ReleaseServer) prepareUpdate(req *hapi.UpdateReleaseRequest) (*release. return currentRelease, updatedRelease, err } -// performUpdateForce performs the same action as a `helm delete && helm install --replace`. +// performUpdateForce performs the same action as a `helm uninstall && helm install --replace`. func (s *ReleaseServer) performUpdateForce(req *hapi.UpdateReleaseRequest) (*release.Release, error) { // find the last release with the given name oldRelease, err := s.Releases.Last(req.Name) @@ -167,9 +167,9 @@ func (s *ReleaseServer) performUpdateForce(req *hapi.UpdateReleaseRequest) (*rel return newRelease, errors.Wrap(err, "failed update prepare step") } - // From here on out, the release is considered to be in StatusDeleting or StatusDeleted + // From here on out, the release is considered to be in StatusUninstalling or StatusUninstalled // state. There is no turning back. - oldRelease.Info.Status = release.StatusDeleting + oldRelease.Info.Status = release.StatusUninstalling oldRelease.Info.Deleted = time.Now() oldRelease.Info.Description = "Deletion in progress (or silently failed)" s.recordRelease(oldRelease, true) @@ -186,12 +186,12 @@ func (s *ReleaseServer) performUpdateForce(req *hapi.UpdateReleaseRequest) (*rel // delete manifests from the old release _, errs := s.deleteRelease(oldRelease) - oldRelease.Info.Status = release.StatusDeleted - oldRelease.Info.Description = "Deletion complete" + oldRelease.Info.Status = release.StatusUninstalled + oldRelease.Info.Description = "Uninstallation complete" s.recordRelease(oldRelease, true) if len(errs) > 0 { - return newRelease, errors.Errorf("upgrade --force successfully deleted the previous release, but encountered %d error(s) and cannot continue: %s", len(errs), joinErrors(errs)) + return newRelease, errors.Errorf("upgrade --force successfully uninstalled the previous release, but encountered %d error(s) and cannot continue: %s", len(errs), joinErrors(errs)) } // post-delete hooks diff --git a/pkg/tiller/release_update_test.go b/pkg/tiller/release_update_test.go index 5cd25eb5c..178522d08 100644 --- a/pkg/tiller/release_update_test.go +++ b/pkg/tiller/release_update_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -22,8 +22,8 @@ import ( "strings" "testing" + "k8s.io/helm/pkg/chart" "k8s.io/helm/pkg/hapi" - "k8s.io/helm/pkg/hapi/chart" "k8s.io/helm/pkg/hapi/release" ) @@ -130,6 +130,7 @@ func TestUpdateRelease_ComplexReuseValues(t *testing.T) { rs := rsFixture(t) installReq := &hapi.InstallReleaseRequest{ + Name: "complex-reuse-values", Namespace: "spaced", Chart: &chart.Chart{ Metadata: &chart.Metadata{Name: "hello"}, @@ -137,7 +138,6 @@ func TestUpdateRelease_ComplexReuseValues(t *testing.T) { {Name: "templates/hello", Data: []byte("hello: world")}, {Name: "templates/hooks", Data: []byte(manifestWithHook)}, }, - Values: []byte("defaultFoo: defaultBar"), }, Values: []byte("foo: bar"), } @@ -156,7 +156,6 @@ func TestUpdateRelease_ComplexReuseValues(t *testing.T) { {Name: "templates/hello", Data: []byte("hello: world")}, {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, }, - Values: []byte("defaultFoo: defaultBar"), }, } @@ -179,7 +178,6 @@ func TestUpdateRelease_ComplexReuseValues(t *testing.T) { {Name: "templates/hello", Data: []byte("hello: world")}, {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, }, - Values: []byte("defaultFoo: defaultBar"), }, Values: []byte("foo2: bar2"), ReuseValues: true, @@ -205,7 +203,6 @@ func TestUpdateRelease_ComplexReuseValues(t *testing.T) { {Name: "templates/hello", Data: []byte("hello: world")}, {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, }, - Values: []byte("defaultFoo: defaultBar"), }, Values: []byte("foo: baz"), ReuseValues: true, @@ -236,7 +233,7 @@ func TestUpdateRelease_ReuseValues(t *testing.T) { {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, }, // Since reuseValues is set, this should get ignored. - Values: []byte("foo: bar\n"), + Values: map[string]interface{}{"foo": "bar"}, }, Values: []byte("name2: val2"), ReuseValues: true, @@ -246,12 +243,11 @@ func TestUpdateRelease_ReuseValues(t *testing.T) { t.Fatalf("Failed updated: %s", err) } // This should have been overwritten with the old value. - expect := "name: value\n" - if res.Chart.Values != nil && !bytes.Equal(res.Chart.Values, []byte(expect)) { - t.Errorf("Expected chart values to be %q, got %q", expect, res.Chart.Values) + if got := res.Chart.Values["name"]; got != "value" { + t.Errorf("Expected chart values 'name' to be 'value', got %q", got) } // This should have the newly-passed overrides and any other computed values. `name: value` comes from release Config via releaseStub() - expect = "name: value\nname2: val2\n" + expect := "name: value\nname2: val2\n" if res.Config != nil && !bytes.Equal(res.Config, []byte(expect)) { t.Errorf("Expected request config to be %q, got %q", expect, res.Config) } @@ -366,7 +362,7 @@ func TestUpdateReleaseFailure_Force(t *testing.T) { if err != nil { t.Errorf("Expected to be able to get previous release") } - if oldStatus := oldRelease.Info.Status; oldStatus != release.StatusDeleted { + if oldStatus := oldRelease.Info.Status; oldStatus != release.StatusUninstalled { t.Errorf("Expected Deleted status on previous Release version. Got %v", oldStatus) } } diff --git a/pkg/tiller/resource_policy.go b/pkg/tiller/resource_policy.go index 66da1283f..cca2391d8 100644 --- a/pkg/tiller/resource_policy.go +++ b/pkg/tiller/resource_policy.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/tlsutil/cfg.go b/pkg/tlsutil/cfg.go index b822b4dc2..f40258739 100644 --- a/pkg/tlsutil/cfg.go +++ b/pkg/tlsutil/cfg.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/tlsutil/tls.go b/pkg/tlsutil/tls.go index bf9befd35..dc123e1e5 100644 --- a/pkg/tlsutil/tls.go +++ b/pkg/tlsutil/tls.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/tlsutil/tlsutil_test.go b/pkg/tlsutil/tlsutil_test.go index 4f04d50ab..a4b3c9c22 100644 --- a/pkg/tlsutil/tlsutil_test.go +++ b/pkg/tlsutil/tlsutil_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/urlutil/urlutil.go b/pkg/urlutil/urlutil.go index fb67708ae..272907de0 100644 --- a/pkg/urlutil/urlutil.go +++ b/pkg/urlutil/urlutil.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/urlutil/urlutil_test.go b/pkg/urlutil/urlutil_test.go index b60c8514c..8e99c1bfb 100644 --- a/pkg/urlutil/urlutil_test.go +++ b/pkg/urlutil/urlutil_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. @@ -65,8 +65,9 @@ func TestEqual(t *testing.T) { func TestExtractHostname(t *testing.T) { tests := map[string]string{ - "http://example.com": "example.com", - "https://example.com/foo": "example.com", + "http://example.com": "example.com", + "https://example.com/foo": "example.com", + "https://example.com:31337/not/with/a/bang/but/a/whimper": "example.com", } for start, expect := range tests { diff --git a/pkg/version/compatible.go b/pkg/version/compatible.go index 735610778..d0516a9d0 100644 --- a/pkg/version/compatible.go +++ b/pkg/version/compatible.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/version/compatible_test.go b/pkg/version/compatible_test.go index adc1c489e..7a3b23a7d 100644 --- a/pkg/version/compatible_test.go +++ b/pkg/version/compatible_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/version/doc.go b/pkg/version/doc.go index 23c9e500d..3b61dd50e 100644 --- a/pkg/version/doc.go +++ b/pkg/version/doc.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/version/version.go b/pkg/version/version.go index 008740a92..296a97741 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/pkg/version/version_test.go b/pkg/version/version_test.go index 990db776f..a42bb6b64 100644 --- a/pkg/version/version_test.go +++ b/pkg/version/version_test.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The Kubernetes Authors All rights reserved. +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. diff --git a/scripts/completions.bash b/scripts/completions.bash index c24f3d257..ededbb791 100644 --- a/scripts/completions.bash +++ b/scripts/completions.bash @@ -904,10 +904,10 @@ _helm_list() flags+=("--date") flags+=("-d") local_nonpersistent_flags+=("--date") - flags+=("--deleted") - local_nonpersistent_flags+=("--deleted") - flags+=("--deleting") - local_nonpersistent_flags+=("--deleting") + flags+=("--uninstalled") + local_nonpersistent_flags+=("--uninstalled") + flags+=("--uninstalling") + local_nonpersistent_flags+=("--uninstalling") flags+=("--deployed") local_nonpersistent_flags+=("--deployed") flags+=("--failed") diff --git a/scripts/coverage.sh b/scripts/coverage.sh index 1863d5835..62d495769 100755 --- a/scripts/coverage.sh +++ b/scripts/coverage.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright 2016 The Kubernetes Authors All rights reserved. +# 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. diff --git a/scripts/sync-repo.sh b/scripts/sync-repo.sh index 3795b1a7c..453102072 100755 --- a/scripts/sync-repo.sh +++ b/scripts/sync-repo.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright 2016 The Kubernetes Authors All rights reserved. +# 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. diff --git a/scripts/update-docs.sh b/scripts/update-docs.sh index e014b537e..d3018be50 100755 --- a/scripts/update-docs.sh +++ b/scripts/update-docs.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright 2017 The Kubernetes Authors All rights reserved. +# 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. diff --git a/scripts/util.sh b/scripts/util.sh index 09caaf972..c1e6c3751 100644 --- a/scripts/util.sh +++ b/scripts/util.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright 2016 The Kubernetes Authors All rights reserved. +# 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. diff --git a/scripts/validate-go.sh b/scripts/validate-go.sh index ba593e2b7..b0a6e2fbd 100755 --- a/scripts/validate-go.sh +++ b/scripts/validate-go.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright 2016 The Kubernetes Authors All rights reserved. +# 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. diff --git a/scripts/validate-license.sh b/scripts/validate-license.sh index d4bb5e126..3d9488fc7 100755 --- a/scripts/validate-license.sh +++ b/scripts/validate-license.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright 2016 The Kubernetes Authors All rights reserved. +# 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. @@ -26,11 +26,16 @@ find_files() { \( -name '*.go' -o -name '*.sh' \) } -failed=($(find_files | xargs grep -L 'Licensed under the Apache License, Version 2.0 (the "License");')) -if (( ${#failed[@]} > 0 )); then +mapfile -t failed_license_header < <(find_files | xargs grep -L 'Licensed under the Apache License, Version 2.0 (the "License")') +if (( ${#failed_license_header[@]} > 0 )); then echo "Some source files are missing license headers." - for f in "${failed[@]}"; do - echo " $f" - done + printf '%s\n' "${failed_license_header[@]}" + exit 1 +fi + +mapfile -t failed_copyright_header < <(find_files | xargs grep -L 'Copyright The Helm Authors.') +if (( ${#failed_copyright_header[@]} > 0 )); then + echo "Some source files are missing the copyright header." + printf '%s\n' "${failed_copyright_header[@]}" exit 1 fi diff --git a/scripts/verify-docs.sh b/scripts/verify-docs.sh index b0b799eac..b176b036e 100755 --- a/scripts/verify-docs.sh +++ b/scripts/verify-docs.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright 2017 The Kubernetes Authors All rights reserved. +# 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.