Move the kustomize binary to its own module.

This commit is contained in:
Jeffrey Regan
2019-09-25 11:01:03 -07:00
parent 2d0c22d6a4
commit b82a8fd316
55 changed files with 334 additions and 87 deletions

14
kustomize/go.mod Normal file
View File

@@ -0,0 +1,14 @@
module sigs.k8s.io/kustomize/kustomize/v3
go 1.12
require (
github.com/pkg/errors v0.8.1
github.com/spf13/cobra v0.0.5
github.com/spf13/pflag v1.0.5
gopkg.in/inf.v0 v0.9.1 // indirect
sigs.k8s.io/kustomize/v3 v3.2.0
sigs.k8s.io/yaml v1.1.0
)
replace sigs.k8s.io/kustomize/v3 v3.2.0 => ../../kustomize

271
kustomize/go.sum Normal file
View File

@@ -0,0 +1,271 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/OpenPeeDeeP/depguard v0.0.0-20180806142446-a69c782687b2/go.mod h1:7/4sitnI9YlQgTLLk734QlzXT8DuHVnAyztLplQjk+o=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M=
github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-critic/go-critic v0.0.0-20181204210945-1df300866540/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA=
github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonpointer v0.19.2 h1:A9+F4Dc/MCNB5jibxf6rRvOvR/iFgQdyNx9eIhnGqq0=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
github.com/go-openapi/spec v0.19.2 h1:SStNd1jRcYtfKCN7R0laGNs80WYYvn5CbBjM2sOmCrE=
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/go-openapi/swag v0.19.2 h1:jvO6bCMBEilGwMfHhrd61zIID4oIFdwb76V17SM88dE=
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4=
github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ=
github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
github.com/go-toolsmith/astfmt v0.0.0-20180903215011-8f8ee99c3086/go.mod h1:mP93XdblcopXwlyN4X4uodxXQhldPGZbcEJIimQHrkg=
github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw=
github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU=
github.com/go-toolsmith/astp v0.0.0-20180903215135-0af7e3c24f30/go.mod h1:SV2ur98SGypH1UjcPpCatrV5hPazG6+IfNHbkDXBRrk=
github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI=
github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoMrjiy4zvdS+Bg6z9jZH82QXwkcgCBX6nOfnmdaHks=
github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc=
github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8=
github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.0 h1:G8O7TerXerS4F6sx9OV7/nRfJdnXgHZu/S/7F2SN+UE=
github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.0.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7 h1:VoRHccBV7zFWlO/I85D1ke/x5Ak9Y7gKm9gkf7LHNhI=
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4=
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
github.com/golangci/errcheck v0.0.0-20181003203344-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0=
github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8=
github.com/golangci/go-tools v0.0.0-20180109140146-af6baa5dc196/go.mod h1:unzUULGw35sjyOYjUt0jMTXqHlZPpPc6e+xfO4cd6mM=
github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3/go.mod h1:JXrF4TWy4tXYn62/9x8Wm/K/dm06p8tCKwFRDPZG/1o=
github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU=
github.com/golangci/gofmt v0.0.0-20181105071733-0b8337e80d98/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU=
github.com/golangci/golangci-lint v1.17.1/go.mod h1:+5sJSl2h3aly+fpmL2meSP8CaSKua2E4Twi9LPy7b1g=
github.com/golangci/gosec v0.0.0-20180901114220-66fb7fc33547/go.mod h1:0qUabqiIQgfmlAmulqxyiGkkyF6/tOGSnY2cnPVwrzU=
github.com/golangci/ineffassign v0.0.0-20180808204949-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU=
github.com/golangci/lint-1 v0.0.0-20180610141402-ee948d087217/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg=
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o=
github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA=
github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bzsHdTM0bsB7+8mt0GUMvjCgwLpTapNZHU8AajI=
github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4=
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367 h1:ScAXWS+TR6MZKex+7Z8rneuSJH+FSDqd6ocQyl+ZHo4=
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/googleapis/gnostic v0.0.0-20170426233943-68f4ded48ba9 h1:Dk8DO58y0wXeNG49dRqzf4HrZAK9lFfTgS/FNtB6NlM=
github.com/googleapis/gnostic v0.0.0-20170426233943-68f4ded48ba9/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3 h1:/UewZcckqhvnnS0C6r3Sher2hSEbVmM6Ogpcjen08+Y=
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v0.0.0-20161130080628-0de1eaf82fa3/go.mod h1:jxZFDH7ILpTPQTk+E2s+z4CUas9lVNjIuKR4c5/zKgM=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/magiconair/properties v1.7.6/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63 h1:nTT4s92Dgz2HlrB2NaMgvlfqHH39OgMhA7z3PK7PGD4=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-ps v0.0.0-20170309133038-4fdf99ab2936/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da h1:ZQGIPjr1iTtUPXZFk8WShqb5G+Qg65VHFLtSvmHh+Mw=
github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/monopole/mdrip v0.2.48/go.mod h1:rzORfdNQ63T/tS95GOFHB+I3OrT+Bjlk8krOc/QiL/8=
github.com/mozilla/tls-observatory v0.0.0-20180409132520-8791a200eb40/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/nbutton23/zxcvbn-go v0.0.0-20160627004424-a22cb81b2ecd/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
github.com/nbutton23/zxcvbn-go v0.0.0-20171102151520-eafdab6b0663/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c h1:Hww8mOyEKTeON4bZn7FrlLismspbPc1teNRUVH7wLQ8=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c h1:eSfnfIuwhxZyULg1NNuZycJcYkjYVGYe7FczwQReM6U=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.4.2 h1:3mYCb7aPxS/RU7TI1y4rkEn1oKmPRjNJLNEXgw7MH2I=
github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pelletier/go-toml v1.1.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE=
github.com/spf13/afero v1.1.0/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.2/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.0.2/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/timakin/bodyclose v0.0.0-20190407043127-4a873e97b2bb/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s=
github.com/valyala/quicktemplate v1.1.1/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4=
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20170915142106-8351a756f30f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20171026204733-164713f0dfce/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f h1:25KHgbfyiSm6vwQLbM3zZIe1v9p/3ea4Rz+nnM5K/i4=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.0.0-20170915090833-1cbadb444a80/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20170915040203-e531a2a1c15f/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190121143147-24cd39ecf745/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
k8s.io/api v0.0.0-20190313235455-40a48860b5ab h1:DG9A67baNpoeweOy2spF1OWHhnVY5KR7/Ek/+U1lVZc=
k8s.io/api v0.0.0-20190313235455-40a48860b5ab/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
k8s.io/apimachinery v0.0.0-20190313205120-d7deff9243b1 h1:IS7K02iBkQXpCeieSiyJjGoLSdVOv2DbPaWHJ+ZtgKg=
k8s.io/apimachinery v0.0.0-20190313205120-d7deff9243b1/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
k8s.io/client-go v11.0.0+incompatible h1:LBbX2+lOwY9flffWlJM7f1Ct8V2SRNiMRDFeiwnJo9o=
k8s.io/client-go v11.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92 h1:PgoMI/L1Nu5Vmvgm+vGheLuxKST8h6FMOqggyAFtHPc=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/kube-openapi v0.0.0-20190603182131-db7b694dc208 h1:5sW+fEHvlJI3Ngolx30CmubFulwH28DhKjGf70Xmtco=
k8s.io/kube-openapi v0.0.0-20190603182131-db7b694dc208/go.mod h1:nfDlWeOsu3pUf4yWGL+ERqohP4YsZcBJXWMK+gkzOA4=
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
mvdan.cc/unparam v0.0.0-20190124213536-fbb59629db34/go.mod h1:H6SUd1XjIs+qQCyskXg5OFSrilMRUkD8ePJpHKDPaeY=
sigs.k8s.io/kustomize/pluginator v1.0.0/go.mod h1:i8HdU5FdH1zDjCKiFf5CNl7slsc0QffyKsY2OuPynJ0=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=

View File

@@ -0,0 +1,238 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package build
import (
"io"
"path/filepath"
"strings"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/ifc"
"sigs.k8s.io/kustomize/v3/pkg/loader"
"sigs.k8s.io/kustomize/v3/pkg/pgmconfig"
"sigs.k8s.io/kustomize/v3/pkg/plugins"
"sigs.k8s.io/kustomize/v3/pkg/resmap"
"sigs.k8s.io/kustomize/v3/pkg/resource"
"sigs.k8s.io/kustomize/v3/pkg/target"
"sigs.k8s.io/kustomize/v3/plugin/builtin"
"sigs.k8s.io/yaml"
)
// Options contain the options for running a build
type Options struct {
kustomizationPath string
outputPath string
loadRestrictor loader.LoadRestrictorFunc
outOrder reorderOutput
}
// NewOptions creates a Options object
func NewOptions(p, o string) *Options {
return &Options{
kustomizationPath: p,
outputPath: o,
loadRestrictor: loader.RestrictionRootOnly,
}
}
var examples = `
To generate the resources specified in 'someDir/kustomization.yaml', run
kustomize build someDir
The default argument to 'build' is '.' (the current working directory).
The argument can be a URL resolving to a directory
with a kustomization.yaml file, e.g.
kustomize build \
github.com/kubernetes-sigs/kustomize//examples/multibases/dev/?ref=v1.0.6
The URL should be formulated as described at
https://github.com/hashicorp/go-getter#url-format
`
// NewCmdBuild creates a new build command.
func NewCmdBuild(
out io.Writer, fSys fs.FileSystem,
v ifc.Validator, rf *resmap.Factory,
ptf resmap.PatchFactory) *cobra.Command {
var o Options
pluginConfig := plugins.DefaultPluginConfig()
pl := plugins.NewLoader(pluginConfig, rf)
cmd := &cobra.Command{
Use: "build {path}",
Short: "Print configuration per contents of " + pgmconfig.KustomizationFileNames[0],
Example: examples,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
err := o.Validate(args)
if err != nil {
return err
}
return o.RunBuild(out, v, fSys, rf, ptf, pl)
},
}
cmd.Flags().StringVarP(
&o.outputPath,
"output", "o", "",
"If specified, write the build output to this path.")
loader.AddFlagLoadRestrictor(cmd.Flags())
plugins.AddFlagEnablePlugins(
cmd.Flags(), &pluginConfig.Enabled)
addFlagReorderOutput(cmd.Flags())
cmd.AddCommand(NewCmdBuildPrune(out, v, fSys, rf, ptf, pl))
return cmd
}
// Validate validates build command.
func (o *Options) Validate(args []string) (err error) {
if len(args) > 1 {
return errors.New(
"specify one path to " + pgmconfig.KustomizationFileNames[0])
}
if len(args) == 0 {
o.kustomizationPath = loader.CWD
} else {
o.kustomizationPath = args[0]
}
o.loadRestrictor, err = loader.ValidateFlagLoadRestrictor()
if err != nil {
return err
}
o.outOrder, err = validateFlagReorderOutput()
return
}
// RunBuild runs build command.
func (o *Options) RunBuild(
out io.Writer, v ifc.Validator, fSys fs.FileSystem,
rf *resmap.Factory, ptf resmap.PatchFactory,
pl *plugins.Loader) error {
ldr, err := loader.NewLoader(
o.loadRestrictor, v, o.kustomizationPath, fSys)
if err != nil {
return err
}
defer ldr.Cleanup()
kt, err := target.NewKustTarget(ldr, rf, ptf, pl)
if err != nil {
return err
}
m, err := kt.MakeCustomizedResMap()
if err != nil {
return err
}
return o.emitResources(out, fSys, m)
}
func (o *Options) RunBuildPrune(
out io.Writer, v ifc.Validator, fSys fs.FileSystem,
rf *resmap.Factory, ptf resmap.PatchFactory,
pl *plugins.Loader) error {
ldr, err := loader.NewLoader(
o.loadRestrictor, v, o.kustomizationPath, fSys)
if err != nil {
return err
}
defer ldr.Cleanup()
kt, err := target.NewKustTarget(ldr, rf, ptf, pl)
if err != nil {
return err
}
m, err := kt.MakePruneConfigMap()
if err != nil {
return err
}
return o.emitResources(out, fSys, m)
}
func (o *Options) emitResources(
out io.Writer, fSys fs.FileSystem, m resmap.ResMap) error {
if o.outputPath != "" && fSys.IsDir(o.outputPath) {
return writeIndividualFiles(fSys, o.outputPath, m)
}
if o.outOrder == legacy {
// Done this way just to show how overall sorting
// can be performed by a plugin. This particular
// plugin doesn't require configuration; just make
// it and call transform.
builtin.NewLegacyOrderTransformerPlugin().Transform(m)
}
res, err := m.AsYaml()
if err != nil {
return err
}
if o.outputPath != "" {
return fSys.WriteFile(o.outputPath, res)
}
_, err = out.Write(res)
return err
}
func NewCmdBuildPrune(
out io.Writer, v ifc.Validator, fSys fs.FileSystem,
rf *resmap.Factory, ptf resmap.PatchFactory,
pl *plugins.Loader) *cobra.Command {
var o Options
cmd := &cobra.Command{
Use: "alpha-inventory [path]",
Short: "Print the inventory object which contains a list of all other objects",
Example: examples,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
err := o.Validate(args)
if err != nil {
return err
}
return o.RunBuildPrune(out, v, fSys, rf, ptf, pl)
},
}
return cmd
}
func writeIndividualFiles(
fSys fs.FileSystem, folderPath string, m resmap.ResMap) error {
byNamespace := m.GroupedByCurrentNamespace()
for namespace, resList := range byNamespace {
for _, res := range resList {
fName := fileName(res)
if len(byNamespace) > 1 {
fName = strings.ToLower(namespace) + "_" + fName
}
err := writeFile(fSys, folderPath, fName, res)
if err != nil {
return err
}
}
}
for _, res := range m.NonNamespaceable() {
err := writeFile(fSys, folderPath, fileName(res), res)
if err != nil {
return err
}
}
return nil
}
func fileName(res *resource.Resource) string {
return strings.ToLower(res.GetGvk().String()) +
"_" + strings.ToLower(res.GetName()) + ".yaml"
}
func writeFile(
fSys fs.FileSystem, path, fName string, res *resource.Resource) error {
out, err := yaml.Marshal(res.Map())
if err != nil {
return err
}
return fSys.WriteFile(filepath.Join(path, fName), out)
}

View File

@@ -0,0 +1,51 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package build
import (
"testing"
"sigs.k8s.io/kustomize/v3/pkg/pgmconfig"
)
func TestNewOptionsToSilenceCodeInspectionError(t *testing.T) {
if NewOptions("foo", "bar") == nil {
t.Fatal("could not make new options")
}
}
func TestBuildValidate(t *testing.T) {
var cases = []struct {
name string
args []string
path string
erMsg string
}{
{"noargs", []string{}, ".", ""},
{"file", []string{"beans"}, "beans", ""},
{"path", []string{"a/b/c"}, "a/b/c", ""},
{"path", []string{"too", "many"},
"", "specify one path to " + pgmconfig.KustomizationFileNames[0]},
}
for _, mycase := range cases {
opts := Options{}
e := opts.Validate(mycase.args)
if len(mycase.erMsg) > 0 {
if e == nil {
t.Errorf("%s: Expected an error %v", mycase.name, mycase.erMsg)
}
if e.Error() != mycase.erMsg {
t.Errorf("%s: Expected error %s, but got %v", mycase.name, mycase.erMsg, e)
}
continue
}
if e != nil {
t.Errorf("%s: unknown error: %v", mycase.name, e)
continue
}
if opts.kustomizationPath != mycase.path {
t.Errorf("%s: expected path '%s', got '%s'", mycase.name, mycase.path, opts.kustomizationPath)
}
}
}

View File

@@ -0,0 +1,50 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package build
import (
"fmt"
"github.com/spf13/pflag"
)
//go:generate stringer -type=reorderOutput
type reorderOutput int
const (
unspecified reorderOutput = iota
none
legacy
)
const (
flagReorderOutputName = "reorder"
)
var (
flagReorderOutputValue = legacy.String()
flagReorderOutputHelp = "Reorder the resources just before output. " +
"Use '" + legacy.String() + "' to apply a legacy reordering (Namespaces first, Webhooks last, etc). " +
"Use '" + none.String() + "' to suppress a final reordering."
)
func addFlagReorderOutput(set *pflag.FlagSet) {
set.StringVar(
&flagReorderOutputValue, flagReorderOutputName,
legacy.String(), flagReorderOutputHelp)
}
func validateFlagReorderOutput() (reorderOutput, error) {
switch flagReorderOutputValue {
case none.String():
return none, nil
case legacy.String():
return legacy, nil
default:
return unspecified, fmt.Errorf(
"illegal flag value --%s %s; legal values: %v",
flagReorderOutputName, flagReorderOutputValue,
[]string{legacy.String(), none.String()})
}
}

View File

@@ -0,0 +1,25 @@
// Code generated by "stringer -type=reorderOutput"; DO NOT EDIT.
package build
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[unspecified-0]
_ = x[none-1]
_ = x[legacy-2]
}
const _reorderOutput_name = "unspecifiednonelegacy"
var _reorderOutput_index = [...]uint8{0, 11, 15, 21}
func (i reorderOutput) String() string {
if i < 0 || i >= reorderOutput(len(_reorderOutput_index)-1) {
return "reorderOutput(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _reorderOutput_name[_reorderOutput_index[i]:_reorderOutput_index[i+1]]
}

View File

@@ -0,0 +1,58 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package commands holds the CLI glue mapping textual commands/args to method calls.
package commands
import (
"flag"
"os"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/build"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/create"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/edit"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/misc"
"sigs.k8s.io/kustomize/v3/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/v3/k8sdeps/transformer"
"sigs.k8s.io/kustomize/v3/k8sdeps/validator"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/pgmconfig"
"sigs.k8s.io/kustomize/v3/pkg/resmap"
"sigs.k8s.io/kustomize/v3/pkg/resource"
)
// NewDefaultCommand returns the default (aka root) command for kustomize command.
func NewDefaultCommand() *cobra.Command {
fSys := fs.MakeRealFS()
stdOut := os.Stdout
c := &cobra.Command{
Use: pgmconfig.ProgramName,
Short: "Manages declarative configuration of Kubernetes",
Long: `
Manages declarative configuration of Kubernetes.
See https://sigs.k8s.io/kustomize
`,
}
uf := kunstruct.NewKunstructuredFactoryImpl()
pf := transformer.NewFactoryImpl()
rf := resmap.NewFactory(resource.NewFactory(uf), pf)
v := validator.NewKustValidator()
c.AddCommand(
build.NewCmdBuild(
stdOut, fSys, v,
rf, pf),
edit.NewCmdEdit(fSys, v, uf),
create.NewCmdCreate(fSys, uf),
misc.NewCmdConfig(fSys),
misc.NewCmdVersion(stdOut),
)
c.PersistentFlags().AddGoFlagSet(flag.CommandLine)
// Workaround for this issue:
// https://github.com/kubernetes/kubernetes/issues/17162
flag.CommandLine.Parse([]string{})
return c
}

View File

@@ -0,0 +1,184 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package create
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/kustfile"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/util"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/ifc"
"sigs.k8s.io/kustomize/v3/pkg/pgmconfig"
)
type createFlags struct {
resources string
namespace string
annotations string
labels string
prefix string
suffix string
detectResources bool
detectRecursive bool
path string
}
// NewCmdCreate returns an instance of 'create' subcommand.
func NewCmdCreate(fSys fs.FileSystem, uf ifc.KunstructuredFactory) *cobra.Command {
opts := createFlags{path: "."}
c := &cobra.Command{
Use: "create",
Short: "Create a new kustomization in the current directory",
Long: "",
Example: `
# Create a new overlay from the base '../base".
kustomize create --resources ../base
# Create a new kustomization detecting resources in the current directory.
kustomize create --autodetect
# Create a new kustomization with multiple resources and fields set.
kustomize create --resources deployment.yaml,service.yaml,../base --namespace staging --nameprefix acme-
`,
RunE: func(cmd *cobra.Command, args []string) error {
return runCreate(opts, fSys, uf)
},
}
c.Flags().StringVar(
&opts.resources,
"resources",
"",
"Name of a file containing a file to add to the kustomization file.")
c.Flags().StringVar(
&opts.namespace,
"namespace",
"",
"Set the value of the namespace field in the customization file.")
c.Flags().StringVar(
&opts.annotations,
"annotations",
"",
"Add one or more common annotations.")
c.Flags().StringVar(
&opts.labels,
"labels",
"",
"Add one or more common labels.")
c.Flags().StringVar(
&opts.prefix,
"nameprefix",
"",
"Sets the value of the namePrefix field in the kustomization file.")
c.Flags().StringVar(
&opts.suffix,
"namesuffix",
"",
"Sets the value of the nameSuffix field in the kustomization file.")
c.Flags().BoolVar(
&opts.detectResources,
"autodetect",
false,
"Search for kubernetes resources in the current directory to be added to the kustomization file.")
c.Flags().BoolVar(
&opts.detectRecursive,
"recursive",
false,
"Enable recursive directory searching for resource auto-detection.")
return c
}
func runCreate(opts createFlags, fSys fs.FileSystem, uf ifc.KunstructuredFactory) error {
var resources []string
var err error
if opts.resources != "" {
resources, err = util.GlobPatterns(fSys, strings.Split(opts.resources, ","))
if err != nil {
return err
}
}
if _, err = kustfile.NewKustomizationFile(fSys); err == nil {
return fmt.Errorf("kustomization file already exists")
}
if opts.detectResources {
detected, err := detectResources(fSys, uf, opts.path, opts.detectRecursive)
if err != nil {
return err
}
for _, resource := range detected {
if kustfile.StringInSlice(resource, resources) {
continue
}
resources = append(resources, resource)
}
}
f, err := fSys.Create("kustomization.yaml")
if err != nil {
return err
}
f.Close()
mf, err := kustfile.NewKustomizationFile(fSys)
if err != nil {
return err
}
m, err := mf.Read()
if err != nil {
return err
}
m.Resources = resources
m.Namespace = opts.namespace
m.NamePrefix = opts.prefix
m.NameSuffix = opts.suffix
annotations, err := util.ConvertToMap(opts.annotations, "annotation")
if err != nil {
return err
}
m.CommonAnnotations = annotations
labels, err := util.ConvertToMap(opts.labels, "label")
if err != nil {
return err
}
m.CommonLabels = labels
return mf.Write(m)
}
func detectResources(fSys fs.FileSystem, uf ifc.KunstructuredFactory, base string, recursive bool) ([]string, error) {
var paths []string
err := fSys.Walk(base, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if path == base {
return nil
}
if info.IsDir() {
if !recursive {
return filepath.SkipDir
}
// If a sub-directory contains an existing kustomization file add the
// directory as a resource and do not decend into it.
for _, kfilename := range pgmconfig.KustomizationFileNames {
if fSys.Exists(filepath.Join(path, kfilename)) {
paths = append(paths, path)
return filepath.SkipDir
}
}
return nil
}
fContents, err := fSys.ReadFile(path)
if err != nil {
return err
}
if _, err := uf.SliceFromBytes(fContents); err != nil {
return nil
}
paths = append(paths, path)
return nil
})
return paths, err
}

View File

@@ -0,0 +1,195 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package create
import (
"reflect"
"testing"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/kustfile"
"sigs.k8s.io/kustomize/v3/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/types"
)
var factory = kunstruct.NewKunstructuredFactoryImpl()
func readKustomizationFS(t *testing.T, fakeFS fs.FileSystem) *types.Kustomization {
kf, err := kustfile.NewKustomizationFile(fakeFS)
if err != nil {
t.Errorf("unexpected new error %v", err)
}
m, err := kf.Read()
if err != nil {
t.Errorf("unexpected read error %v", err)
}
return m
}
func TestCreateNoArgs(t *testing.T) {
fakeFS := fs.MakeFakeFS()
cmd := NewCmdCreate(fakeFS, factory)
err := cmd.RunE(cmd, []string{})
if err != nil {
t.Errorf("unexpected cmd error: %v", err)
}
readKustomizationFS(t, fakeFS)
}
func TestCreateWithResources(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.WriteFile("foo.yaml", []byte(""))
fakeFS.WriteFile("bar.yaml", []byte(""))
opts := createFlags{resources: "foo.yaml,bar.yaml"}
err := runCreate(opts, fakeFS, factory)
if err != nil {
t.Errorf("unexpected cmd error: %v", err)
}
m := readKustomizationFS(t, fakeFS)
expected := []string{"foo.yaml", "bar.yaml"}
if !reflect.DeepEqual(m.Resources, expected) {
t.Fatalf("expected %+v but got %+v", expected, m.Resources)
}
}
func TestCreateWithNamespace(t *testing.T) {
fakeFS := fs.MakeFakeFS()
want := "foo"
opts := createFlags{namespace: want}
err := runCreate(opts, fakeFS, factory)
if err != nil {
t.Errorf("unexpected cmd error: %v", err)
}
m := readKustomizationFS(t, fakeFS)
got := m.Namespace
if got != want {
t.Errorf("want: %s, got: %s", want, got)
}
}
func TestCreateWithLabels(t *testing.T) {
fakeFS := fs.MakeFakeFS()
opts := createFlags{labels: "foo:bar"}
err := runCreate(opts, fakeFS, factory)
if err != nil {
t.Errorf("unexpected cmd error: %v", err)
}
m := readKustomizationFS(t, fakeFS)
expected := map[string]string{"foo": "bar"}
if !reflect.DeepEqual(m.CommonLabels, expected) {
t.Fatalf("expected %+v but got %+v", expected, m.CommonLabels)
}
}
func TestCreateWithAnnotations(t *testing.T) {
fakeFS := fs.MakeFakeFS()
opts := createFlags{annotations: "foo:bar"}
err := runCreate(opts, fakeFS, factory)
if err != nil {
t.Errorf("unexpected cmd error: %v", err)
}
m := readKustomizationFS(t, fakeFS)
expected := map[string]string{"foo": "bar"}
if !reflect.DeepEqual(m.CommonAnnotations, expected) {
t.Fatalf("expected %+v but got %+v", expected, m.CommonAnnotations)
}
}
func TestCreateWithNamePrefix(t *testing.T) {
fakeFS := fs.MakeFakeFS()
want := "foo-"
opts := createFlags{prefix: want}
err := runCreate(opts, fakeFS, factory)
if err != nil {
t.Errorf("unexpected cmd error: %v", err)
}
m := readKustomizationFS(t, fakeFS)
got := m.NamePrefix
if got != want {
t.Errorf("want: %s, got: %s", want, got)
}
}
func TestCreateWithNameSuffix(t *testing.T) {
fakeFS := fs.MakeFakeFS()
opts := createFlags{suffix: "-foo"}
err := runCreate(opts, fakeFS, factory)
if err != nil {
t.Errorf("unexpected cmd error: %v", err)
}
m := readKustomizationFS(t, fakeFS)
if m.NameSuffix != "-foo" {
t.Errorf("want: -foo, got: %s", m.NameSuffix)
}
}
func writeDetectContent(fakeFS fs.FileSystem) {
fakeFS.WriteFile("/test.yaml", []byte(`
apiVersion: v1
kind: Service
metadata:
name: test`))
fakeFS.WriteFile("/README.md", []byte(`
# Not a k8s resource
This file is not a valid kubernetes object.`))
fakeFS.WriteFile("/non-k8s.yaml", []byte(`
# Not a k8s resource
other: yaml
foo:
- bar
- baz`))
fakeFS.Mkdir("/sub")
fakeFS.WriteFile("/sub/test.yaml", []byte(`
apiVersion: v1
kind: Service
metadata:
name: test2`))
fakeFS.WriteFile("/sub/README.md", []byte(`
# Not a k8s resource
This file in a subdirectory is not a valid kubernetes object.`))
fakeFS.WriteFile("/sub/non-k8s.yaml", []byte(`
# Not a k8s resource
other: yaml
foo:
- bar
- baz`))
fakeFS.Mkdir("/overlay")
fakeFS.WriteFile("/overlay/test.yaml", []byte(`
apiVersion: v1
kind: Service
metadata:
name: test3`))
fakeFS.WriteFile("/overlay/kustomization.yaml", []byte(`
resources:
- test.yaml`))
}
func TestCreateWithDetect(t *testing.T) {
fakeFS := fs.MakeFakeFS()
writeDetectContent(fakeFS)
opts := createFlags{path: "/", detectResources: true}
err := runCreate(opts, fakeFS, factory)
if err != nil {
t.Fatalf("unexpected cmd error: %v", err)
}
m := readKustomizationFS(t, fakeFS)
expected := []string{"/test.yaml"}
if !reflect.DeepEqual(m.Resources, expected) {
t.Fatalf("expected %+v but got %+v", expected, m.Resources)
}
}
func TestCreateWithDetectRecursive(t *testing.T) {
fakeFS := fs.MakeFakeFS()
writeDetectContent(fakeFS)
opts := createFlags{path: "/", detectResources: true, detectRecursive: true}
err := runCreate(opts, fakeFS, factory)
if err != nil {
t.Fatalf("unexpected cmd error: %v", err)
}
m := readKustomizationFS(t, fakeFS)
expected := []string{"/overlay", "/sub/test.yaml", "/test.yaml"}
if !reflect.DeepEqual(m.Resources, expected) {
t.Fatalf("expected %+v but got %+v", expected, m.Resources)
}
}

View File

@@ -0,0 +1,84 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package add
import (
"errors"
"fmt"
"strings"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/kustfile"
"sigs.k8s.io/kustomize/v3/pkg/fs"
)
type addBaseOptions struct {
baseDirectoryPaths string
}
// newCmdAddBase adds the file path of the kustomize base to the kustomization file.
func newCmdAddBase(fsys fs.FileSystem) *cobra.Command {
var o addBaseOptions
cmd := &cobra.Command{
Use: "base",
Short: "Adds one or more bases to the kustomization.yaml in current directory",
Example: `
add base {filepath1},{filepath2}`,
RunE: func(cmd *cobra.Command, args []string) error {
err := o.Validate(args)
if err != nil {
return err
}
err = o.Complete(cmd, args)
if err != nil {
return err
}
return o.RunAddBase(fsys)
},
}
return cmd
}
// Validate validates addBase command.
func (o *addBaseOptions) Validate(args []string) error {
if len(args) != 1 {
return errors.New("must specify a base directory")
}
o.baseDirectoryPaths = args[0]
return nil
}
// Complete completes addBase command.
func (o *addBaseOptions) Complete(cmd *cobra.Command, args []string) error {
return nil
}
// RunAddBase runs addBase command (do real work).
func (o *addBaseOptions) RunAddBase(fSys fs.FileSystem) error {
mf, err := kustfile.NewKustomizationFile(fSys)
if err != nil {
return err
}
m, err := mf.Read()
if err != nil {
return err
}
// split directory paths
paths := strings.Split(o.baseDirectoryPaths, ",")
for _, path := range paths {
if !fSys.Exists(path) {
return errors.New(path + " does not exist")
}
if kustfile.StringInSlice(path, m.Resources) {
return fmt.Errorf("base %s already in kustomization file", path)
}
m.Resources = append(m.Resources, path)
}
return mf.Write(m)
}

View File

@@ -0,0 +1,86 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package add
import (
"strings"
"testing"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/kustfile"
"sigs.k8s.io/kustomize/v3/pkg/fs"
)
const (
baseDirectoryPaths = "my/path/to/wonderful/base,other/path/to/even/more/wonderful/base"
)
func TestAddBaseHappyPath(t *testing.T) {
fakeFS := fs.MakeFakeFS()
bases := strings.Split(baseDirectoryPaths, ",")
for _, base := range bases {
fakeFS.Mkdir(base)
}
fakeFS.WriteTestKustomization()
cmd := newCmdAddBase(fakeFS)
args := []string{baseDirectoryPaths}
err := cmd.RunE(cmd, args)
if err != nil {
t.Errorf("unexpected cmd error: %v", err)
}
content, err := fakeFS.ReadTestKustomization()
if err != nil {
t.Errorf("unexpected read error: %v", err)
}
for _, base := range bases {
if !strings.Contains(string(content), base) {
t.Errorf("expected base name in kustomization")
}
}
}
func TestAddBaseAlreadyThere(t *testing.T) {
fakeFS := fs.MakeFakeFS()
// Create fake directories
bases := strings.Split(baseDirectoryPaths, ",")
for _, base := range bases {
fakeFS.Mkdir(base)
}
fakeFS.WriteTestKustomization()
cmd := newCmdAddBase(fakeFS)
args := []string{baseDirectoryPaths}
err := cmd.RunE(cmd, args)
if err != nil {
t.Fatalf("unexpected cmd error: %v", err)
}
// adding an existing base should return an error
err = cmd.RunE(cmd, args)
if err == nil {
t.Errorf("expected already there problem")
}
var expectedErrors []string
for _, base := range bases {
msg := "base " + base + " already in kustomization file"
expectedErrors = append(expectedErrors, msg)
if !kustfile.StringInSlice(msg, expectedErrors) {
t.Errorf("unexpected error %v", err)
}
}
}
func TestAddBaseNoArgs(t *testing.T) {
fakeFS := fs.MakeFakeFS()
cmd := newCmdAddBase(fakeFS)
err := cmd.Execute()
if err == nil {
t.Errorf("expected error: %v", err)
}
if err.Error() != "must specify a base directory" {
t.Errorf("incorrect error: %v", err.Error())
}
}

View File

@@ -0,0 +1,145 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package add
import (
"fmt"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/kustfile"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/util"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/pgmconfig"
"sigs.k8s.io/kustomize/v3/pkg/types"
)
// kindOfAdd is the kind of metadata being added: label or annotation
type kindOfAdd int
const (
annotation kindOfAdd = iota
label
)
func (k kindOfAdd) String() string {
kinds := [...]string{
"annotation",
"label",
}
if k < 0 || k > 1 {
return "Unknown metadatakind"
}
return kinds[k]
}
type addMetadataOptions struct {
force bool
metadata map[string]string
mapValidator func(map[string]string) error
kind kindOfAdd
}
// newCmdAddAnnotation adds one or more commonAnnotations to the kustomization file.
func newCmdAddAnnotation(fSys fs.FileSystem, v func(map[string]string) error) *cobra.Command {
var o addMetadataOptions
o.kind = annotation
o.mapValidator = v
cmd := &cobra.Command{
Use: "annotation",
Short: "Adds one or more commonAnnotations to " + pgmconfig.KustomizationFileNames[0],
Example: `
add annotation {annotationKey1:annotationValue1},{annotationKey2:annotationValue2}`,
RunE: func(cmd *cobra.Command, args []string) error {
return o.runE(args, fSys, o.addAnnotations)
},
}
cmd.Flags().BoolVarP(&o.force, "force", "f", false,
"overwrite commonAnnotation if it already exists",
)
return cmd
}
// newCmdAddLabel adds one or more commonLabels to the kustomization file.
func newCmdAddLabel(fSys fs.FileSystem, v func(map[string]string) error) *cobra.Command {
var o addMetadataOptions
o.kind = label
o.mapValidator = v
cmd := &cobra.Command{
Use: "label",
Short: "Adds one or more commonLabels to " + pgmconfig.KustomizationFileNames[0],
Example: `
add label {labelKey1:labelValue1},{labelKey2:labelValue2}`,
RunE: func(cmd *cobra.Command, args []string) error {
return o.runE(args, fSys, o.addLabels)
},
}
cmd.Flags().BoolVarP(&o.force, "force", "f", false,
"overwrite commonLabel if it already exists",
)
return cmd
}
func (o *addMetadataOptions) runE(
args []string, fSys fs.FileSystem, adder func(*types.Kustomization) error) error {
err := o.validateAndParse(args)
if err != nil {
return err
}
kf, err := kustfile.NewKustomizationFile(fSys)
if err != nil {
return err
}
m, err := kf.Read()
if err != nil {
return err
}
err = adder(m)
if err != nil {
return err
}
return kf.Write(m)
}
// validateAndParse validates `add` commands and parses them into o.metadata
func (o *addMetadataOptions) validateAndParse(args []string) error {
if len(args) < 1 {
return fmt.Errorf("must specify %s", o.kind)
}
if len(args) > 1 {
return fmt.Errorf("%ss must be comma-separated, with no spaces", o.kind)
}
m, err := util.ConvertToMap(args[0], o.kind.String())
if err != nil {
return err
}
if err = o.mapValidator(m); err != nil {
return err
}
o.metadata = m
return nil
}
func (o *addMetadataOptions) addAnnotations(m *types.Kustomization) error {
if m.CommonAnnotations == nil {
m.CommonAnnotations = make(map[string]string)
}
return o.writeToMap(m.CommonAnnotations, annotation)
}
func (o *addMetadataOptions) addLabels(m *types.Kustomization) error {
if m.CommonLabels == nil {
m.CommonLabels = make(map[string]string)
}
return o.writeToMap(m.CommonLabels, label)
}
func (o *addMetadataOptions) writeToMap(m map[string]string, kind kindOfAdd) error {
for k, v := range o.metadata {
if _, ok := m[k]; ok && !o.force {
return fmt.Errorf("%s %s already in kustomization file", kind, k)
}
m[k] = v
}
return nil
}

View File

@@ -0,0 +1,350 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package add
import (
"testing"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/kustfile"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/types"
"sigs.k8s.io/kustomize/v3/pkg/validators"
)
func makeKustomization(t *testing.T) *types.Kustomization {
fakeFS := fs.MakeFakeFS()
fakeFS.WriteTestKustomization()
kf, err := kustfile.NewKustomizationFile(fakeFS)
if err != nil {
t.Errorf("unexpected new error %v", err)
}
m, err := kf.Read()
if err != nil {
t.Errorf("unexpected read error %v", err)
}
return m
}
func TestRunAddAnnotation(t *testing.T) {
var o addMetadataOptions
o.metadata = map[string]string{"owls": "cute", "otters": "adorable"}
m := makeKustomization(t)
err := o.addAnnotations(m)
if err != nil {
t.Errorf("unexpected error: could not write to kustomization file")
}
// adding the same test input should not work
err = o.addAnnotations(m)
if err == nil {
t.Errorf("expected already in kustomization file error")
}
// adding new annotations should work
o.metadata = map[string]string{"new": "annotation"}
err = o.addAnnotations(m)
if err != nil {
t.Errorf("unexpected error: could not write to kustomization file")
}
}
func TestAddAnnotationNoArgs(t *testing.T) {
fakeFS := fs.MakeFakeFS()
v := validators.MakeHappyMapValidator(t)
cmd := newCmdAddAnnotation(fakeFS, v.Validator)
err := cmd.Execute()
v.VerifyNoCall()
if err == nil {
t.Errorf("expected an error")
}
if err.Error() != "must specify annotation" {
t.Errorf("incorrect error: %v", err.Error())
}
}
func TestAddAnnotationInvalidFormat(t *testing.T) {
fakeFS := fs.MakeFakeFS()
v := validators.MakeSadMapValidator(t)
cmd := newCmdAddAnnotation(fakeFS, v.Validator)
args := []string{"whatever:whatever"}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err == nil {
t.Errorf("expected an error")
}
if err.Error() != validators.SAD {
t.Errorf("incorrect error: %v", err.Error())
}
}
func TestAddAnnotationManyArgs(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.WriteTestKustomization()
v := validators.MakeHappyMapValidator(t)
cmd := newCmdAddAnnotation(fakeFS, v.Validator)
args := []string{"k1:v1,k2:v2,k3:v3,k4:v5"}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err != nil {
t.Errorf("unexpected error: %v", err.Error())
}
}
func TestAddAnnotationValueQuoted(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.WriteTestKustomization()
v := validators.MakeHappyMapValidator(t)
cmd := newCmdAddAnnotation(fakeFS, v.Validator)
args := []string{"k1:\"v1\""}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err != nil {
t.Errorf("unexpected error: %v", err.Error())
}
}
func TestAddAnnotationValueWithColon(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.WriteTestKustomization()
v := validators.MakeHappyMapValidator(t)
cmd := newCmdAddAnnotation(fakeFS, v.Validator)
args := []string{"k1:\"v1:v2\""}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err != nil {
t.Errorf("unexpected error: %v", err.Error())
}
}
func TestAddAnnotationNoKey(t *testing.T) {
fakeFS := fs.MakeFakeFS()
v := validators.MakeHappyMapValidator(t)
cmd := newCmdAddAnnotation(fakeFS, v.Validator)
args := []string{":nokey"}
err := cmd.RunE(cmd, args)
v.VerifyNoCall()
if err == nil {
t.Errorf("expected an error")
}
if err.Error() != "invalid annotation: ':nokey' (need k:v pair where v may be quoted)" {
t.Errorf("incorrect error: %v", err.Error())
}
}
func TestAddAnnotationTooManyColons(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.WriteTestKustomization()
v := validators.MakeHappyMapValidator(t)
cmd := newCmdAddAnnotation(fakeFS, v.Validator)
args := []string{"key:v1:v2"}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err != nil {
t.Errorf("unexpected error: %v", err.Error())
}
}
func TestAddAnnotationNoValue(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.WriteTestKustomization()
v := validators.MakeHappyMapValidator(t)
cmd := newCmdAddAnnotation(fakeFS, v.Validator)
args := []string{"no:,value"}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err != nil {
t.Errorf("unexpected error: %v", err.Error())
}
}
func TestAddAnnotationMultipleArgs(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.WriteTestKustomization()
v := validators.MakeHappyMapValidator(t)
cmd := newCmdAddAnnotation(fakeFS, v.Validator)
args := []string{"this:annotation", "has:spaces"}
err := cmd.RunE(cmd, args)
v.VerifyNoCall()
if err == nil {
t.Errorf("expected an error")
}
if err.Error() != "annotations must be comma-separated, with no spaces" {
t.Errorf("incorrect error: %v", err.Error())
}
}
func TestAddAnnotationForce(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.WriteTestKustomization()
v := validators.MakeHappyMapValidator(t)
cmd := newCmdAddAnnotation(fakeFS, v.Validator)
args := []string{"key:foo"}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err != nil {
t.Errorf("unexpected error: %v", err.Error())
}
// trying to add the same annotation again should not work
args = []string{"key:bar"}
v = validators.MakeHappyMapValidator(t)
cmd = newCmdAddAnnotation(fakeFS, v.Validator)
err = cmd.RunE(cmd, args)
v.VerifyCall()
if err == nil {
t.Errorf("expected an error")
}
if err.Error() != "annotation key already in kustomization file" {
t.Errorf("expected an error")
}
// but trying to add it with --force should
v = validators.MakeHappyMapValidator(t)
cmd = newCmdAddAnnotation(fakeFS, v.Validator)
cmd.Flag("force").Value.Set("true")
err = cmd.RunE(cmd, args)
v.VerifyCall()
if err != nil {
t.Errorf("unexpected error: %v", err.Error())
}
}
func TestRunAddLabel(t *testing.T) {
var o addMetadataOptions
o.metadata = map[string]string{"owls": "cute", "otters": "adorable"}
m := makeKustomization(t)
err := o.addLabels(m)
if err != nil {
t.Errorf("unexpected error: could not write to kustomization file")
}
// adding the same test input should not work
err = o.addLabels(m)
if err == nil {
t.Errorf("expected already in kustomization file error")
}
// adding new labels should work
o.metadata = map[string]string{"new": "label"}
err = o.addLabels(m)
if err != nil {
t.Errorf("unexpected error: could not write to kustomization file")
}
}
func TestAddLabelNoArgs(t *testing.T) {
fakeFS := fs.MakeFakeFS()
v := validators.MakeHappyMapValidator(t)
cmd := newCmdAddLabel(fakeFS, v.Validator)
err := cmd.Execute()
v.VerifyNoCall()
if err == nil {
t.Errorf("expected an error")
}
if err.Error() != "must specify label" {
t.Errorf("incorrect error: %v", err.Error())
}
}
func TestAddLabelInvalidFormat(t *testing.T) {
fakeFS := fs.MakeFakeFS()
v := validators.MakeSadMapValidator(t)
cmd := newCmdAddLabel(fakeFS, v.Validator)
args := []string{"exclamation!:point"}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err == nil {
t.Errorf("expected an error")
}
if err.Error() != validators.SAD {
t.Errorf("incorrect error: %v", err.Error())
}
}
func TestAddLabelNoKey(t *testing.T) {
fakeFS := fs.MakeFakeFS()
v := validators.MakeHappyMapValidator(t)
cmd := newCmdAddLabel(fakeFS, v.Validator)
args := []string{":nokey"}
err := cmd.RunE(cmd, args)
v.VerifyNoCall()
if err == nil {
t.Errorf("expected an error")
}
if err.Error() != "invalid label: ':nokey' (need k:v pair where v may be quoted)" {
t.Errorf("incorrect error: %v", err.Error())
}
}
func TestAddLabelTooManyColons(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.WriteTestKustomization()
v := validators.MakeHappyMapValidator(t)
cmd := newCmdAddLabel(fakeFS, v.Validator)
args := []string{"key:v1:v2"}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err != nil {
t.Errorf("unexpected error: %v", err.Error())
}
}
func TestAddLabelNoValue(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.WriteTestKustomization()
v := validators.MakeHappyMapValidator(t)
cmd := newCmdAddLabel(fakeFS, v.Validator)
args := []string{"no,value:"}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err != nil {
t.Errorf("unexpected error: %v", err.Error())
}
}
func TestAddLabelMultipleArgs(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.WriteTestKustomization()
v := validators.MakeHappyMapValidator(t)
cmd := newCmdAddLabel(fakeFS, v.Validator)
args := []string{"this:input", "has:spaces"}
err := cmd.RunE(cmd, args)
v.VerifyNoCall()
if err == nil {
t.Errorf("expected an error")
}
if err.Error() != "labels must be comma-separated, with no spaces" {
t.Errorf("incorrect error: %v", err.Error())
}
}
func TestAddLabelForce(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.WriteTestKustomization()
v := validators.MakeHappyMapValidator(t)
cmd := newCmdAddLabel(fakeFS, v.Validator)
args := []string{"key:foo"}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err != nil {
t.Errorf("unexpected error: %v", err.Error())
}
// trying to add the same label again should not work
args = []string{"key:bar"}
v = validators.MakeHappyMapValidator(t)
cmd = newCmdAddLabel(fakeFS, v.Validator)
err = cmd.RunE(cmd, args)
v.VerifyCall()
if err == nil {
t.Errorf("expected an error")
}
if err.Error() != "label key already in kustomization file" {
t.Errorf("expected an error")
}
// but trying to add it with --force should
v = validators.MakeHappyMapValidator(t)
cmd = newCmdAddLabel(fakeFS, v.Validator)
cmd.Flag("force").Value.Set("true")
err = cmd.RunE(cmd, args)
v.VerifyCall()
if err != nil {
t.Errorf("unexpected error: %v", err.Error())
}
}

View File

@@ -0,0 +1,88 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package add
import (
"errors"
"log"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/kustfile"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/util"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/patch"
)
type addPatchOptions struct {
patchFilePaths []string
}
// newCmdAddPatch adds the name of a file containing a patch to the kustomization file.
func newCmdAddPatch(fsys fs.FileSystem) *cobra.Command {
var o addPatchOptions
cmd := &cobra.Command{
Use: "patch",
Short: "Add the name of a file containing a patch to the kustomization file.",
Example: `
add patch {filepath}`,
RunE: func(cmd *cobra.Command, args []string) error {
err := o.Validate(args)
if err != nil {
return err
}
err = o.Complete(cmd, args)
if err != nil {
return err
}
return o.RunAddPatch(fsys)
},
}
return cmd
}
// Validate validates addPatch command.
func (o *addPatchOptions) Validate(args []string) error {
if len(args) == 0 {
return errors.New("must specify a patch file")
}
o.patchFilePaths = args
return nil
}
// Complete completes addPatch command.
func (o *addPatchOptions) Complete(cmd *cobra.Command, args []string) error {
return nil
}
// RunAddPatch runs addPatch command (do real work).
func (o *addPatchOptions) RunAddPatch(fSys fs.FileSystem) error {
patches, err := util.GlobPatterns(fSys, o.patchFilePaths)
if err != nil {
return err
}
if len(patches) == 0 {
return nil
}
mf, err := kustfile.NewKustomizationFile(fSys)
if err != nil {
return err
}
m, err := mf.Read()
if err != nil {
return err
}
for _, p := range patches {
if patch.Exist(m.PatchesStrategicMerge, p) {
log.Printf("patch %s already in kustomization file", p)
continue
}
m.PatchesStrategicMerge = patch.Append(m.PatchesStrategicMerge, p)
}
return mf.Write(m)
}

View File

@@ -0,0 +1,75 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package add
import (
"strings"
"testing"
"sigs.k8s.io/kustomize/v3/pkg/fs"
)
const (
patchFileName = "myWonderfulPatch.yaml"
patchFileContent = `
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
`
)
func TestAddPatchHappyPath(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.WriteFile(patchFileName, []byte(patchFileContent))
fakeFS.WriteFile(patchFileName+"another", []byte(patchFileContent))
fakeFS.WriteTestKustomization()
cmd := newCmdAddPatch(fakeFS)
args := []string{patchFileName + "*"}
err := cmd.RunE(cmd, args)
if err != nil {
t.Errorf("unexpected cmd error: %v", err)
}
content, err := fakeFS.ReadTestKustomization()
if err != nil {
t.Errorf("unexpected read error: %v", err)
}
if !strings.Contains(string(content), patchFileName) {
t.Errorf("expected patch name in kustomization")
}
if !strings.Contains(string(content), patchFileName+"another") {
t.Errorf("expected patch name in kustomization")
}
}
func TestAddPatchAlreadyThere(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.WriteFile(patchFileName, []byte(patchFileContent))
fakeFS.WriteTestKustomization()
cmd := newCmdAddPatch(fakeFS)
args := []string{patchFileName}
err := cmd.RunE(cmd, args)
if err != nil {
t.Fatalf("unexpected cmd error: %v", err)
}
// adding an existing patch shouldn't return an error
err = cmd.RunE(cmd, args)
if err != nil {
t.Errorf("unexpected cmd error: %v", err)
}
}
func TestAddPatchNoArgs(t *testing.T) {
fakeFS := fs.MakeFakeFS()
cmd := newCmdAddPatch(fakeFS)
err := cmd.Execute()
if err == nil {
t.Errorf("expected error: %v", err)
}
if err.Error() != "must specify a patch file" {
t.Errorf("incorrect error: %v", err.Error())
}
}

View File

@@ -0,0 +1,87 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package add
import (
"errors"
"log"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/kustfile"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/util"
"sigs.k8s.io/kustomize/v3/pkg/fs"
)
type addResourceOptions struct {
resourceFilePaths []string
}
// newCmdAddResource adds the name of a file containing a resource to the kustomization file.
func newCmdAddResource(fsys fs.FileSystem) *cobra.Command {
var o addResourceOptions
cmd := &cobra.Command{
Use: "resource",
Short: "Add the name of a file containing a resource to the kustomization file.",
Example: `
add resource {filepath}`,
RunE: func(cmd *cobra.Command, args []string) error {
err := o.Validate(args)
if err != nil {
return err
}
err = o.Complete(cmd, args)
if err != nil {
return err
}
return o.RunAddResource(fsys)
},
}
return cmd
}
// Validate validates addResource command.
func (o *addResourceOptions) Validate(args []string) error {
if len(args) == 0 {
return errors.New("must specify a resource file")
}
o.resourceFilePaths = args
return nil
}
// Complete completes addResource command.
func (o *addResourceOptions) Complete(cmd *cobra.Command, args []string) error {
return nil
}
// RunAddResource runs addResource command (do real work).
func (o *addResourceOptions) RunAddResource(fSys fs.FileSystem) error {
resources, err := util.GlobPatterns(fSys, o.resourceFilePaths)
if err != nil {
return err
}
if len(resources) == 0 {
return nil
}
mf, err := kustfile.NewKustomizationFile(fSys)
if err != nil {
return err
}
m, err := mf.Read()
if err != nil {
return err
}
for _, resource := range resources {
if kustfile.StringInSlice(resource, m.Resources) {
log.Printf("resource %s already in kustomization file", resource)
continue
}
m.Resources = append(m.Resources, resource)
}
return mf.Write(m)
}

View File

@@ -0,0 +1,75 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package add
import (
"strings"
"testing"
"sigs.k8s.io/kustomize/v3/pkg/fs"
)
const (
resourceFileName = "myWonderfulResource.yaml"
resourceFileContent = `
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
`
)
func TestAddResourceHappyPath(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.WriteFile(resourceFileName, []byte(resourceFileContent))
fakeFS.WriteFile(resourceFileName+"another", []byte(resourceFileContent))
fakeFS.WriteTestKustomization()
cmd := newCmdAddResource(fakeFS)
args := []string{resourceFileName + "*"}
err := cmd.RunE(cmd, args)
if err != nil {
t.Errorf("unexpected cmd error: %v", err)
}
content, err := fakeFS.ReadTestKustomization()
if err != nil {
t.Errorf("unexpected read error: %v", err)
}
if !strings.Contains(string(content), resourceFileName) {
t.Errorf("expected resource name in kustomization")
}
if !strings.Contains(string(content), resourceFileName+"another") {
t.Errorf("expected resource name in kustomization")
}
}
func TestAddResourceAlreadyThere(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.WriteFile(resourceFileName, []byte(resourceFileContent))
fakeFS.WriteTestKustomization()
cmd := newCmdAddResource(fakeFS)
args := []string{resourceFileName}
err := cmd.RunE(cmd, args)
if err != nil {
t.Fatalf("unexpected cmd error: %v", err)
}
// adding an existing resource doesn't return an error
err = cmd.RunE(cmd, args)
if err != nil {
t.Errorf("unexpected cmd error :%v", err)
}
}
func TestAddResourceNoArgs(t *testing.T) {
fakeFS := fs.MakeFakeFS()
cmd := newCmdAddResource(fakeFS)
err := cmd.Execute()
if err == nil {
t.Errorf("expected error: %v", err)
}
if err.Error() != "must specify a resource file" {
t.Errorf("incorrect error: %v", err.Error())
}
}

View File

@@ -0,0 +1,56 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package add
import (
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/ifc"
)
// NewCmdAdd returns an instance of 'add' subcommand.
func NewCmdAdd(
fSys fs.FileSystem,
ldr ifc.Loader,
kf ifc.KunstructuredFactory) *cobra.Command {
c := &cobra.Command{
Use: "add",
Short: "Adds an item to the kustomization file.",
Long: "",
Example: `
# Adds a secret to the kustomization file
kustomize edit add secret NAME --from-literal=k=v
# Adds a configmap to the kustomization file
kustomize edit add configmap NAME --from-literal=k=v
# Adds a resource to the kustomization
kustomize edit add resource <filepath>
# Adds a patch to the kustomization
kustomize edit add patch <filepath>
# Adds one or more base directories to the kustomization
kustomize edit add base <filepath>
kustomize edit add base <filepath1>,<filepath2>,<filepath3>
# Adds one or more commonLabels to the kustomization
kustomize edit add label {labelKey1:labelValue1},{labelKey2:labelValue2}
# Adds one or more commonAnnotations to the kustomization
kustomize edit add annotation {annotationKey1:annotationValue1},{annotationKey2:annotationValue2}
`,
Args: cobra.MinimumNArgs(1),
}
c.AddCommand(
newCmdAddResource(fSys),
newCmdAddPatch(fSys),
newCmdAddSecret(fSys, ldr, kf),
newCmdAddConfigMap(fSys, ldr, kf),
newCmdAddBase(fSys),
newCmdAddLabel(fSys, ldr.Validator().MakeLabelValidator()),
newCmdAddAnnotation(fSys, ldr.Validator().MakeAnnotationValidator()),
)
return c
}

View File

@@ -0,0 +1,130 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package add
import (
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/kustfile"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/ifc"
"sigs.k8s.io/kustomize/v3/pkg/types"
)
// newCmdAddConfigMap returns a new command.
func newCmdAddConfigMap(
fSys fs.FileSystem,
ldr ifc.Loader,
kf ifc.KunstructuredFactory) *cobra.Command {
var flags flagsAndArgs
cmd := &cobra.Command{
Use: "configmap NAME [--from-file=[key=]source] [--from-literal=key1=value1]",
Short: "Adds a configmap to the kustomization file.",
Long: "",
Example: `
# Adds a configmap to the kustomization file (with a specified key)
kustomize edit add configmap my-configmap --from-file=my-key=file/path --from-literal=my-literal=12345
# Adds a configmap to the kustomization file (key is the filename)
kustomize edit add configmap my-configmap --from-file=file/path
# Adds a configmap from env-file
kustomize edit add configmap my-configmap --from-env-file=env/path.env
`,
RunE: func(_ *cobra.Command, args []string) error {
err := flags.ExpandFileSource(fSys)
if err != nil {
return err
}
err = flags.Validate(args)
if err != nil {
return err
}
// Load the kustomization file.
mf, err := kustfile.NewKustomizationFile(fSys)
if err != nil {
return err
}
kustomization, err := mf.Read()
if err != nil {
return err
}
// Add the flagsAndArgs map to the kustomization file.
err = addConfigMap(ldr, kustomization, flags, kf)
if err != nil {
return err
}
// Write out the kustomization file with added configmap.
return mf.Write(kustomization)
},
}
cmd.Flags().StringSliceVar(
&flags.FileSources,
"from-file",
[]string{},
"Key file can be specified using its file path, in which case file basename will be used as configmap "+
"key, or optionally with a key and file path, in which case the given key will be used. Specifying a "+
"directory will iterate each named file in the directory whose basename is a valid configmap key.")
cmd.Flags().StringArrayVar(
&flags.LiteralSources,
"from-literal",
[]string{},
"Specify a key and literal value to insert in configmap (i.e. mykey=somevalue)")
cmd.Flags().StringVar(
&flags.EnvFileSource,
"from-env-file",
"",
"Specify the path to a file to read lines of key=val pairs to create a configmap (i.e. a Docker .env file).")
return cmd
}
// addConfigMap adds a configmap to a kustomization file.
// Note: error may leave kustomization file in an undefined state.
// Suggest passing a copy of kustomization file.
func addConfigMap(
ldr ifc.Loader,
k *types.Kustomization,
flags flagsAndArgs, kf ifc.KunstructuredFactory) error {
args := findOrMakeConfigMapArgs(k, flags.Name)
mergeFlagsIntoCmArgs(args, flags)
// Validate by trying to create corev1.configmap.
_, err := kf.MakeConfigMap(ldr, k.GeneratorOptions, args)
if err != nil {
return err
}
return nil
}
func findOrMakeConfigMapArgs(m *types.Kustomization, name string) *types.ConfigMapArgs {
for i, v := range m.ConfigMapGenerator {
if name == v.Name {
return &m.ConfigMapGenerator[i]
}
}
// config map not found, create new one and add it to the kustomization file.
cm := &types.ConfigMapArgs{GeneratorArgs: types.GeneratorArgs{Name: name}}
m.ConfigMapGenerator = append(m.ConfigMapGenerator, *cm)
return &m.ConfigMapGenerator[len(m.ConfigMapGenerator)-1]
}
func mergeFlagsIntoCmArgs(args *types.ConfigMapArgs, flags flagsAndArgs) {
if len(flags.LiteralSources) > 0 {
args.LiteralSources = append(
args.LiteralSources, flags.LiteralSources...)
}
if len(flags.FileSources) > 0 {
args.FileSources = append(
args.FileSources, flags.FileSources...)
}
if flags.EnvFileSource != "" {
args.EnvSources = append(
args.EnvSources, flags.EnvFileSource)
}
}

View File

@@ -0,0 +1,107 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package add
import (
"testing"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/loader"
"sigs.k8s.io/kustomize/v3/pkg/types"
"sigs.k8s.io/kustomize/v3/pkg/validators"
)
func TestNewAddConfigMapIsNotNil(t *testing.T) {
fSys := fs.MakeFakeFS()
ldr := loader.NewFileLoaderAtCwd(validators.MakeFakeValidator(), fSys)
if newCmdAddConfigMap(fSys, ldr, nil) == nil {
t.Fatal("newCmdAddConfigMap shouldn't be nil")
}
}
func TestMakeConfigMapArgs(t *testing.T) {
cmName := "test-config-name"
kustomization := &types.Kustomization{
NamePrefix: "test-name-prefix",
}
if len(kustomization.ConfigMapGenerator) != 0 {
t.Fatal("Initial kustomization should not have any configmaps")
}
args := findOrMakeConfigMapArgs(kustomization, cmName)
if args == nil {
t.Fatalf("args should always be non-nil")
}
if len(kustomization.ConfigMapGenerator) != 1 {
t.Fatalf("Kustomization should have newly created configmap")
}
if &kustomization.ConfigMapGenerator[len(kustomization.ConfigMapGenerator)-1] != args {
t.Fatalf("Pointer address for newly inserted configmap generator should be same")
}
args2 := findOrMakeConfigMapArgs(kustomization, cmName)
if args2 != args {
t.Fatalf("should have returned an existing args with name: %v", cmName)
}
if len(kustomization.ConfigMapGenerator) != 1 {
t.Fatalf("Should not insert configmap for an existing name: %v", cmName)
}
}
func TestMergeFlagsIntoConfigMapArgs_LiteralSources(t *testing.T) {
k := &types.Kustomization{}
args := findOrMakeConfigMapArgs(k, "foo")
mergeFlagsIntoGeneratorArgs(
&args.GeneratorArgs,
flagsAndArgs{LiteralSources: []string{"k1=v1"}})
mergeFlagsIntoGeneratorArgs(
&args.GeneratorArgs,
flagsAndArgs{LiteralSources: []string{"k2=v2"}})
if k.ConfigMapGenerator[0].LiteralSources[0] != "k1=v1" {
t.Fatalf("expected v1")
}
if k.ConfigMapGenerator[0].LiteralSources[1] != "k2=v2" {
t.Fatalf("expected v2")
}
}
func TestMergeFlagsIntoConfigMapArgs_FileSources(t *testing.T) {
k := &types.Kustomization{}
args := findOrMakeConfigMapArgs(k, "foo")
mergeFlagsIntoGeneratorArgs(
&args.GeneratorArgs,
flagsAndArgs{FileSources: []string{"file1"}})
mergeFlagsIntoGeneratorArgs(
&args.GeneratorArgs,
flagsAndArgs{FileSources: []string{"file2"}})
if k.ConfigMapGenerator[0].FileSources[0] != "file1" {
t.Fatalf("expected file1")
}
if k.ConfigMapGenerator[0].FileSources[1] != "file2" {
t.Fatalf("expected file2")
}
}
func TestMergeFlagsIntoConfigMapArgs_EnvSource(t *testing.T) {
k := &types.Kustomization{}
args := findOrMakeConfigMapArgs(k, "foo")
mergeFlagsIntoGeneratorArgs(
&args.GeneratorArgs,
flagsAndArgs{EnvFileSource: "env1"})
mergeFlagsIntoGeneratorArgs(
&args.GeneratorArgs,
flagsAndArgs{EnvFileSource: "env2"})
if k.ConfigMapGenerator[0].EnvSources[0] != "env1" {
t.Fatalf("expected env1")
}
if k.ConfigMapGenerator[0].EnvSources[1] != "env2" {
t.Fatalf("expected env2")
}
}

View File

@@ -0,0 +1,95 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package add
import (
"fmt"
"strings"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/util"
"sigs.k8s.io/kustomize/v3/pkg/fs"
)
// flagsAndArgs encapsulates the options for add secret/configmap commands.
type flagsAndArgs struct {
// Name of configMap/Secret (required)
Name string
// FileSources to derive the configMap/Secret from (optional)
FileSources []string
// LiteralSources to derive the configMap/Secret from (optional)
LiteralSources []string
// EnvFileSource to derive the configMap/Secret from (optional)
// TODO: Rationalize this name with Generic.EnvSource
EnvFileSource string
// Type of secret to create
Type string
}
// Validate validates required fields are set to support structured generation.
func (a *flagsAndArgs) Validate(args []string) error {
if len(args) != 1 {
return fmt.Errorf("name must be specified once")
}
a.Name = args[0]
if len(a.EnvFileSource) == 0 && len(a.FileSources) == 0 && len(a.LiteralSources) == 0 {
return fmt.Errorf("at least from-env-file, or from-file or from-literal must be set")
}
if len(a.EnvFileSource) > 0 && (len(a.FileSources) > 0 || len(a.LiteralSources) > 0) {
return fmt.Errorf("from-env-file cannot be combined with from-file or from-literal")
}
// TODO: Should we check if the path exists? if it's valid, if it's within the same (sub-)directory?
return nil
}
// ExpandFileSource normalizes a string list, possibly
// containing globs, into a validated, globless list.
// For example, this list:
// some/path
// some/dir/a*
// bfile=some/dir/b*
// becomes:
// some/path
// some/dir/airplane
// some/dir/ant
// some/dir/apple
// bfile=some/dir/banana
// i.e. everything is converted to a key=value pair,
// where the value is always a relative file path,
// and the key, if missing, is the same as the value.
// In the case where the key is explicitly declared,
// the globbing, if present, must have exactly one match.
func (a *flagsAndArgs) ExpandFileSource(fSys fs.FileSystem) error {
var results []string
for _, pattern := range a.FileSources {
var patterns []string
key := ""
// check if the pattern is in `--from-file=[key=]source` format
// and if so split it to send only the file-pattern to glob function
s := strings.Split(pattern, "=")
if len(s) == 2 {
patterns = append(patterns, s[1])
key = s[0]
} else {
patterns = append(patterns, s[0])
}
result, err := util.GlobPatterns(fSys, patterns)
if err != nil {
return err
}
// if the format is `--from-file=[key=]source` accept only one result
// and extend it with the `key=` prefix
if key != "" {
if len(result) != 1 {
return fmt.Errorf(
"'pattern '%s' catches files %v, should catch only one", pattern, result)
}
fileSource := fmt.Sprintf("%s=%s", key, result[0])
results = append(results, fileSource)
} else {
results = append(results, result...)
}
}
a.FileSources = results
return nil
}

View File

@@ -0,0 +1,125 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package add
import (
"reflect"
"testing"
"sigs.k8s.io/kustomize/v3/pkg/fs"
)
func TestDataValidation_NoName(t *testing.T) {
fa := flagsAndArgs{}
if fa.Validate([]string{}) == nil {
t.Fatal("Validation should fail if no name is specified")
}
}
func TestDataValidation_MoreThanOneName(t *testing.T) {
fa := flagsAndArgs{}
if fa.Validate([]string{"name", "othername"}) == nil {
t.Fatal("Validation should fail if more than one name is specified")
}
}
func TestDataConfigValidation_Flags(t *testing.T) {
tests := []struct {
name string
fa flagsAndArgs
shouldFail bool
}{
{
name: "env-file-source and literal are both set",
fa: flagsAndArgs{
LiteralSources: []string{"one", "two"},
EnvFileSource: "three",
},
shouldFail: true,
},
{
name: "env-file-source and from-file are both set",
fa: flagsAndArgs{
FileSources: []string{"one", "two"},
EnvFileSource: "three",
},
shouldFail: true,
},
{
name: "we don't have any option set",
fa: flagsAndArgs{},
shouldFail: true,
},
{
name: "we have from-file and literal ",
fa: flagsAndArgs{
LiteralSources: []string{"one", "two"},
FileSources: []string{"three", "four"},
},
shouldFail: false,
},
}
for _, test := range tests {
if test.fa.Validate([]string{"name"}) == nil && test.shouldFail {
t.Fatalf("Validation should fail if %s", test.name)
} else if test.fa.Validate([]string{"name"}) != nil && !test.shouldFail {
t.Fatalf("Validation should succeed if %s", test.name)
}
}
}
func TestExpandFileSource(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.Create("dir/fa1")
fakeFS.Create("dir/fa2")
fakeFS.Create("dir/readme")
fa := flagsAndArgs{
FileSources: []string{"dir/fa*"},
}
fa.ExpandFileSource(fakeFS)
expected := []string{
"dir/fa1",
"dir/fa2",
}
if !reflect.DeepEqual(fa.FileSources, expected) {
t.Fatalf("FileSources is not correctly expanded: %v", fa.FileSources)
}
}
func TestExpandFileSourceWithKey(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.Create("dir/faaaaaaaaaabbbbbbbbbccccccccccccccccc")
fakeFS.Create("dir/foobar")
fakeFS.Create("dir/simplebar")
fakeFS.Create("dir/readme")
fa := flagsAndArgs{
FileSources: []string{"foo-key=dir/fa*", "bar-key=dir/foobar", "dir/simplebar"},
}
fa.ExpandFileSource(fakeFS)
expected := []string{
"foo-key=dir/faaaaaaaaaabbbbbbbbbccccccccccccccccc",
"bar-key=dir/foobar",
"dir/simplebar",
}
if !reflect.DeepEqual(fa.FileSources, expected) {
t.Fatalf("FileSources is not correctly expanded: %v", fa.FileSources)
}
}
func TestExpandFileSourceWithKeyAndError(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.Create("dir/fa1")
fakeFS.Create("dir/fa2")
fakeFS.Create("dir/readme")
fa := flagsAndArgs{
FileSources: []string{"foo-key=dir/fa*"},
}
err := fa.ExpandFileSource(fakeFS)
if err == nil {
t.Fatalf("FileSources should not be correctly expanded: %v", fa.FileSources)
}
}

View File

@@ -0,0 +1,137 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package add
import (
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/kustfile"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/ifc"
"sigs.k8s.io/kustomize/v3/pkg/types"
)
// newCmdAddSecret returns a new command.
func newCmdAddSecret(
fSys fs.FileSystem,
ldr ifc.Loader,
kf ifc.KunstructuredFactory) *cobra.Command {
var flags flagsAndArgs
cmd := &cobra.Command{
Use: "secret NAME [--from-file=[key=]source] [--from-literal=key1=value1] [--type=Opaque|kubernetes.io/tls]",
Short: "Adds a secret to the kustomization file.",
Long: "",
Example: `
# Adds a secret to the kustomization file (with a specified key)
kustomize edit add secret my-secret --from-file=my-key=file/path --from-literal=my-literal=12345
# Adds a secret to the kustomization file (key is the filename)
kustomize edit add secret my-secret --from-file=file/path
# Adds a secret from env-file
kustomize edit add secret my-secret --from-env-file=env/path.env
`,
RunE: func(_ *cobra.Command, args []string) error {
err := flags.ExpandFileSource(fSys)
if err != nil {
return err
}
err = flags.Validate(args)
if err != nil {
return err
}
// Load the kustomization file.
mf, err := kustfile.NewKustomizationFile(fSys)
if err != nil {
return err
}
kustomization, err := mf.Read()
if err != nil {
return err
}
// Add the flagsAndArgs map to the kustomization file.
err = addSecret(ldr, kustomization, flags, kf)
if err != nil {
return err
}
// Write out the kustomization file with added secret.
return mf.Write(kustomization)
},
}
cmd.Flags().StringSliceVar(
&flags.FileSources,
"from-file",
[]string{},
"Key file can be specified using its file path, in which case file basename will be used as secret "+
"key, or optionally with a key and file path, in which case the given key will be used. Specifying a "+
"directory will iterate each named file in the directory whose basename is a valid secret key.")
cmd.Flags().StringArrayVar(
&flags.LiteralSources,
"from-literal",
[]string{},
"Specify a key and literal value to insert in secret (i.e. mykey=somevalue)")
cmd.Flags().StringVar(
&flags.EnvFileSource,
"from-env-file",
"",
"Specify the path to a file to read lines of key=val pairs to create a secret (i.e. a Docker .env file).")
cmd.Flags().StringVar(
&flags.Type,
"type",
"Opaque",
"Specify the secret type this can be 'Opaque' (default), or 'kubernetes.io/tls'")
return cmd
}
// addSecret adds a secret to a kustomization file.
// Note: error may leave kustomization file in an undefined state.
// Suggest passing a copy of kustomization file.
func addSecret(
ldr ifc.Loader,
k *types.Kustomization,
flags flagsAndArgs, kf ifc.KunstructuredFactory) error {
args := findOrMakeSecretArgs(k, flags.Name, flags.Type)
mergeFlagsIntoGeneratorArgs(&args.GeneratorArgs, flags)
// Validate by trying to create corev1.secret.
_, err := kf.MakeSecret(ldr, k.GeneratorOptions, args)
if err != nil {
return err
}
return nil
}
func findOrMakeSecretArgs(m *types.Kustomization, name, secretType string) *types.SecretArgs {
for i, v := range m.SecretGenerator {
if name == v.Name {
return &m.SecretGenerator[i]
}
}
// secret not found, create new one and add it to the kustomization file.
secret := &types.SecretArgs{
GeneratorArgs: types.GeneratorArgs{Name: name},
Type: secretType}
m.SecretGenerator = append(m.SecretGenerator, *secret)
return &m.SecretGenerator[len(m.SecretGenerator)-1]
}
func mergeFlagsIntoGeneratorArgs(args *types.GeneratorArgs, flags flagsAndArgs) {
if len(flags.LiteralSources) > 0 {
args.LiteralSources = append(
args.LiteralSources, flags.LiteralSources...)
}
if len(flags.FileSources) > 0 {
args.FileSources = append(
args.FileSources, flags.FileSources...)
}
if flags.EnvFileSource != "" {
args.EnvSources = append(
args.EnvSources, flags.EnvFileSource)
}
}

View File

@@ -0,0 +1,109 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package add
import (
"testing"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/loader"
"sigs.k8s.io/kustomize/v3/pkg/types"
"sigs.k8s.io/kustomize/v3/pkg/validators"
)
func TestNewCmdAddSecretIsNotNil(t *testing.T) {
fSys := fs.MakeFakeFS()
ldr := loader.NewFileLoaderAtCwd(validators.MakeFakeValidator(), fSys)
if newCmdAddSecret(fSys, ldr, nil) == nil {
t.Fatal("newCmdAddSecret shouldn't be nil")
}
}
func TestMakeSecretArgs(t *testing.T) {
secretName := "test-secret-name"
kustomization := &types.Kustomization{
NamePrefix: "test-name-prefix",
}
secretType := "Opaque"
if len(kustomization.SecretGenerator) != 0 {
t.Fatal("Initial kustomization should not have any secrets")
}
args := findOrMakeSecretArgs(kustomization, secretName, secretType)
if args == nil {
t.Fatalf("args should always be non-nil")
}
if len(kustomization.SecretGenerator) != 1 {
t.Fatalf("Kustomization should have newly created secret")
}
if &kustomization.SecretGenerator[len(kustomization.SecretGenerator)-1] != args {
t.Fatalf("Pointer address for newly inserted secret generator should be same")
}
args2 := findOrMakeSecretArgs(kustomization, secretName, secretType)
if args2 != args {
t.Fatalf("should have returned an existing args with name: %v", secretName)
}
if len(kustomization.SecretGenerator) != 1 {
t.Fatalf("Should not insert secret for an existing name: %v", secretName)
}
}
func TestMergeFlagsIntoSecretArgs_LiteralSources(t *testing.T) {
k := &types.Kustomization{}
args := findOrMakeSecretArgs(k, "foo", "forbidden")
mergeFlagsIntoGeneratorArgs(
&args.GeneratorArgs,
flagsAndArgs{LiteralSources: []string{"k1=v1"}})
mergeFlagsIntoGeneratorArgs(
&args.GeneratorArgs,
flagsAndArgs{LiteralSources: []string{"k2=v2"}})
if k.SecretGenerator[0].LiteralSources[0] != "k1=v1" {
t.Fatalf("expected v1")
}
if k.SecretGenerator[0].LiteralSources[1] != "k2=v2" {
t.Fatalf("expected v2")
}
}
func TestMergeFlagsIntoSecretArgs_FileSources(t *testing.T) {
k := &types.Kustomization{}
args := findOrMakeSecretArgs(k, "foo", "forbidden")
mergeFlagsIntoGeneratorArgs(
&args.GeneratorArgs,
flagsAndArgs{FileSources: []string{"file1"}})
mergeFlagsIntoGeneratorArgs(
&args.GeneratorArgs,
flagsAndArgs{FileSources: []string{"file2"}})
if k.SecretGenerator[0].FileSources[0] != "file1" {
t.Fatalf("expected file1")
}
if k.SecretGenerator[0].FileSources[1] != "file2" {
t.Fatalf("expected file2")
}
}
func TestMergeFlagsIntoSecretArgs_EnvSource(t *testing.T) {
k := &types.Kustomization{}
args := findOrMakeSecretArgs(k, "foo", "forbidden")
mergeFlagsIntoGeneratorArgs(
&args.GeneratorArgs,
flagsAndArgs{EnvFileSource: "env1"})
mergeFlagsIntoGeneratorArgs(
&args.GeneratorArgs,
flagsAndArgs{EnvFileSource: "env2"})
if k.SecretGenerator[0].EnvSources[0] != "env1" {
t.Fatalf("expected env1")
}
if k.SecretGenerator[0].EnvSources[1] != "env2" {
t.Fatalf("expected env2")
}
}

View File

@@ -0,0 +1,44 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package edit
import (
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/edit/add"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/edit/fix"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/edit/remove"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/edit/set"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/ifc"
"sigs.k8s.io/kustomize/v3/pkg/loader"
)
// NewCmdEdit returns an instance of 'edit' subcommand.
func NewCmdEdit(
fSys fs.FileSystem, v ifc.Validator, kf ifc.KunstructuredFactory) *cobra.Command {
c := &cobra.Command{
Use: "edit",
Short: "Edits a kustomization file",
Long: "",
Example: `
# Adds a configmap to the kustomization file
kustomize edit add configmap NAME --from-literal=k=v
# Sets the nameprefix field
kustomize edit set nameprefix <prefix-value>
# Sets the namesuffix field
kustomize edit set namesuffix <suffix-value>
`,
Args: cobra.MinimumNArgs(1),
}
c.AddCommand(
add.NewCmdAdd(fSys, loader.NewFileLoaderAtCwd(v, fSys), kf),
set.NewCmdSet(fSys, v),
fix.NewCmdFix(fSys),
remove.NewCmdRemove(fSys, loader.NewFileLoaderAtCwd(v, fSys)),
)
return c
}

View File

@@ -0,0 +1,43 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package fix
import (
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/kustfile"
"sigs.k8s.io/kustomize/v3/pkg/fs"
)
// NewCmdFix returns an instance of 'fix' subcommand.
func NewCmdFix(fSys fs.FileSystem) *cobra.Command {
cmd := &cobra.Command{
Use: "fix",
Short: "Fix the missing fields in kustomization file",
Long: "",
Example: `
# Fix the missing and deprecated fields in kustomization file
kustomize edit fix
`,
RunE: func(cmd *cobra.Command, args []string) error {
return RunFix(fSys)
},
}
return cmd
}
// RunFix runs `fix` command
func RunFix(fSys fs.FileSystem) error {
mf, err := kustfile.NewKustomizationFile(fSys)
if err != nil {
return err
}
m, err := mf.Read()
if err != nil {
return err
}
return mf.Write(m)
}

View File

@@ -0,0 +1,32 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package fix
import (
"strings"
"testing"
"sigs.k8s.io/kustomize/v3/pkg/fs"
)
func TestFix(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.WriteTestKustomizationWith([]byte(`nameprefix: some-prefix-`))
cmd := NewCmdFix(fakeFS)
err := cmd.RunE(cmd, nil)
if err != nil {
t.Errorf("unexpected cmd error: %v", err)
}
content, err := fakeFS.ReadTestKustomization()
if err != nil {
t.Errorf("unexpected read error: %v", err)
}
if !strings.Contains(string(content), "apiVersion: ") {
t.Errorf("expected apiVersion in kustomization")
}
if !strings.Contains(string(content), "kind: Kustomization") {
t.Errorf("expected kind in kustomization")
}
}

View File

@@ -0,0 +1,43 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package remove
import (
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/ifc"
)
// NewCmdRemove returns an instance of 'remove' subcommand.
func NewCmdRemove(
fsys fs.FileSystem,
ldr ifc.Loader) *cobra.Command {
c := &cobra.Command{
Use: "remove",
Short: "Removes items from the kustomization file.",
Long: "",
Example: `
# Removes resources from the kustomization file
kustomize edit remove resource {filepath} {filepath}
kustomize edit remove resource {pattern}
# Removes one or more patches from the kustomization file
kustomize edit remove patch <filepath>
# Removes one or more commonLabels from the kustomization file
kustomize edit remove label {labelKey1},{labelKey2}
# Removes one or more commonAnnotations from the kustomization file
kustomize edit remove annotation {annotationKey1},{annotationKey2}
`,
Args: cobra.MinimumNArgs(1),
}
c.AddCommand(
newCmdRemoveResource(fsys),
newCmdRemoveLabel(fsys, ldr.Validator().MakeLabelNameValidator()),
newCmdRemoveAnnotation(fsys, ldr.Validator().MakeAnnotationNameValidator()),
newCmdRemovePatch(fsys),
)
return c
}

View File

@@ -0,0 +1,162 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package remove
import (
"fmt"
"strings"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/kustfile"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/pgmconfig"
"sigs.k8s.io/kustomize/v3/pkg/types"
)
// kindOfAdd is the kind of metadata being added: label or annotation
type kindOfAdd int
const (
annotation kindOfAdd = iota
label
)
func (k kindOfAdd) String() string {
kinds := [...]string{
"annotation",
"label",
}
if k < 0 || k > 1 {
return "Unknown metadatakind"
}
return kinds[k]
}
type removeMetadataOptions struct {
ignore bool
metadata []string
arrayValidator func([]string) error
kind kindOfAdd
}
// newCmdRemoveLabel removes one or more commonAnnotations from the kustomization file.
func newCmdRemoveAnnotation(fSys fs.FileSystem, v func([]string) error) *cobra.Command {
var o removeMetadataOptions
o.kind = label
o.arrayValidator = v
cmd := &cobra.Command{
Use: "annotation",
Short: "Removes one or more commonAnnotations from " + pgmconfig.KustomizationFileNames[0],
Example: `
remove annotation {annotationKey1},{annotationKey2}`,
RunE: func(cmd *cobra.Command, args []string) error {
return o.runE(args, fSys, o.removeAnnotations)
},
}
cmd.Flags().BoolVarP(&o.ignore, "ignore-non-existence", "i", false,
"ignore error if the given label doesn't exist",
)
return cmd
}
// newCmdRemoveLabel removes one or more commonLabels from the kustomization file.
func newCmdRemoveLabel(fSys fs.FileSystem, v func([]string) error) *cobra.Command {
var o removeMetadataOptions
o.kind = label
o.arrayValidator = v
cmd := &cobra.Command{
Use: "label",
Short: "Removes one or more commonLabels from " + pgmconfig.KustomizationFileNames[0],
Example: `
remove label {labelKey1},{labelKey2}`,
RunE: func(cmd *cobra.Command, args []string) error {
return o.runE(args, fSys, o.removeLabels)
},
}
cmd.Flags().BoolVarP(&o.ignore, "ignore-non-existence", "i", false,
"ignore error if the given label doesn't exist",
)
return cmd
}
func (o *removeMetadataOptions) runE(
args []string, fSys fs.FileSystem, remover func(*types.Kustomization) error) error {
err := o.validateAndParse(args)
if err != nil {
return err
}
kf, err := kustfile.NewKustomizationFile(fSys)
if err != nil {
return err
}
m, err := kf.Read()
if err != nil {
return err
}
err = remover(m)
if err != nil {
return err
}
return kf.Write(m)
}
// validateAndParse validates `remove` commands and parses them into o.metadata
func (o *removeMetadataOptions) validateAndParse(args []string) error {
if len(args) < 1 {
return fmt.Errorf("must specify %s", o.kind)
}
if len(args) > 1 {
return fmt.Errorf("%ss must be comma-separated, with no spaces", o.kind)
}
m, err := o.convertToArray(args[0])
if err != nil {
return err
}
if err = o.arrayValidator(m); err != nil {
return err
}
o.metadata = m
return nil
}
func (o *removeMetadataOptions) convertToArray(arg string) ([]string, error) {
inputs := strings.Split(arg, ",")
result := make([]string, 0, len(inputs))
for _, input := range inputs {
if len(input) == 0 {
return nil, o.makeError(input, "name is empty")
}
result = append(result, input)
}
return result, nil
}
func (o *removeMetadataOptions) removeAnnotations(m *types.Kustomization) error {
if m.CommonAnnotations == nil && !o.ignore {
return fmt.Errorf("commonAnnotations is not defined in kustomization file")
}
return o.removeFromMap(m.CommonAnnotations, annotation)
}
func (o *removeMetadataOptions) removeLabels(m *types.Kustomization) error {
if m.CommonLabels == nil && !o.ignore {
return fmt.Errorf("commonLabels is not defined in kustomization file")
}
return o.removeFromMap(m.CommonLabels, label)
}
func (o *removeMetadataOptions) removeFromMap(m map[string]string, kind kindOfAdd) error {
for _, k := range o.metadata {
if _, ok := m[k]; !ok && !o.ignore {
return fmt.Errorf("%s %s is not defined in kustomization file", kind, k)
}
delete(m, k)
}
return nil
}
func (o *removeMetadataOptions) makeError(input string, message string) error {
return fmt.Errorf("invalid %s: '%s' (%s)", o.kind, input, message)
}

View File

@@ -0,0 +1,338 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package remove
import (
"fmt"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/kustfile"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/types"
"sigs.k8s.io/kustomize/v3/pkg/validators"
"strings"
"testing"
)
func makeKustomizationFS() fs.FileSystem {
fakeFS := fs.MakeFakeFS()
commonLabels := []string{"label1: val1", "label2: val2"}
commonAnnotations := []string{"annotation1: val1", "annotation2: val2"}
fakeFS.WriteTestKustomizationWith([]byte(
fmt.Sprintf("commonLabels:\n %s\ncommonAnnotations:\n %s",
strings.Join(commonLabels, "\n "), strings.Join(commonAnnotations, "\n "))))
return fakeFS
}
func readKustomizationFS(t *testing.T, fakeFS fs.FileSystem) *types.Kustomization {
kf, err := kustfile.NewKustomizationFile(fakeFS)
if err != nil {
t.Errorf("unexpected new error %v", err)
}
m, err := kf.Read()
if err != nil {
t.Errorf("unexpected read error %v", err)
}
return m
}
func makeKustomization(t *testing.T) *types.Kustomization {
fakeFS := makeKustomizationFS()
return readKustomizationFS(t, fakeFS)
}
func TestRemoveAnnotation(t *testing.T) {
var o removeMetadataOptions
o.metadata = []string{"annotation1"}
m := makeKustomization(t)
err := o.removeAnnotations(m)
if err != nil {
t.Errorf("unexpected error: could not write to kustomization file")
}
// adding the same test input should not work
err = o.removeAnnotations(m)
if err == nil {
t.Errorf("expected not exist in kustomization file error")
}
_, exists := m.CommonAnnotations["annotation1"]
if exists {
t.Errorf("annotation1 must be deleted")
}
_, exists = m.CommonAnnotations["annotation2"]
if !exists {
t.Errorf("annotation2 must exist")
}
}
func TestRemoveAnnotationIgnore(t *testing.T) {
fakeFS := makeKustomizationFS()
v := validators.MakeHappyMapValidator(t)
cmd := newCmdRemoveAnnotation(fakeFS, v.ValidatorArray)
cmd.Flag("ignore-non-existence").Value.Set("true")
args := []string{"annotation3"}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err != nil {
t.Errorf("unexpected error %v", err)
}
}
func TestRemoveAnnotationNoDefinition(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.WriteTestKustomizationWith([]byte(""))
v := validators.MakeHappyMapValidator(t)
cmd := newCmdRemoveAnnotation(fakeFS, v.ValidatorArray)
args := []string{"annotation1,annotation2"}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err == nil {
t.Errorf("expected an error")
}
if err.Error() != "commonAnnotations is not defined in kustomization file" {
t.Errorf("incorrect error: %v", err.Error())
}
}
func TestRemoveAnnotationNoDefinitionIgnore(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.WriteTestKustomizationWith([]byte(""))
v := validators.MakeHappyMapValidator(t)
cmd := newCmdRemoveLabel(fakeFS, v.ValidatorArray)
cmd.Flag("ignore-non-existence").Value.Set("true")
args := []string{"annotation1,annotation2"}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err != nil {
t.Errorf("unexpected error %v", err)
}
}
func TestRemoveAnnotationNoArgs(t *testing.T) {
fakeFS := makeKustomizationFS()
v := validators.MakeHappyMapValidator(t)
cmd := newCmdRemoveAnnotation(fakeFS, v.ValidatorArray)
err := cmd.Execute()
v.VerifyNoCall()
if err == nil {
t.Errorf("expected an error")
}
if err.Error() != "must specify label" {
t.Errorf("incorrect error: %v", err.Error())
}
}
func TestRemoveAnnotationInvalidFormat(t *testing.T) {
fakeFS := makeKustomizationFS()
v := validators.MakeSadMapValidator(t)
cmd := newCmdRemoveAnnotation(fakeFS, v.ValidatorArray)
args := []string{"nospecialchars%^=@"}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err == nil {
t.Errorf("expected an error")
}
if err.Error() != validators.SAD {
t.Errorf("incorrect error: %v", err.Error())
}
}
func TestRemoveAnnotationMultipleArgs(t *testing.T) {
fakeFS := makeKustomizationFS()
v := validators.MakeHappyMapValidator(t)
cmd := newCmdRemoveAnnotation(fakeFS, v.ValidatorArray)
args := []string{"annotation1,annotation2"}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err != nil {
t.Errorf("unexpected error %v", err)
}
m := readKustomizationFS(t, fakeFS)
splitArgs := strings.Split(args[0], ",")
for _, k := range splitArgs {
if _, exist := m.CommonAnnotations[k]; exist {
t.Errorf("%s must be deleted", k)
}
}
}
func TestRemoveAnnotationMultipleArgsInvalidFormat(t *testing.T) {
fakeFS := makeKustomizationFS()
v := validators.MakeSadMapValidator(t)
cmd := newCmdRemoveAnnotation(fakeFS, v.ValidatorArray)
args := []string{"annotation1", "annotation2"}
err := cmd.RunE(cmd, args)
v.VerifyNoCall()
if err == nil {
t.Errorf("expected an error")
}
if err.Error() != "labels must be comma-separated, with no spaces" {
t.Errorf("incorrect error: %v", err.Error())
}
}
func TestRemoveLabel(t *testing.T) {
var o removeMetadataOptions
o.metadata = []string{"label1"}
m := makeKustomization(t)
err := o.removeLabels(m)
if err != nil {
t.Errorf("unexpected error: could not write to kustomization file")
}
// adding the same test input should not work
err = o.removeLabels(m)
if err == nil {
t.Errorf("expected not exist in kustomization file error")
}
_, exists := m.CommonLabels["label1"]
if exists {
t.Errorf("label1 must be deleted")
}
_, exists = m.CommonLabels["label2"]
if !exists {
t.Errorf("label2 must exist")
}
}
func TestRemoveLabelIgnore(t *testing.T) {
fakeFS := makeKustomizationFS()
v := validators.MakeHappyMapValidator(t)
cmd := newCmdRemoveLabel(fakeFS, v.ValidatorArray)
cmd.Flag("ignore-non-existence").Value.Set("true")
args := []string{"label3"}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err != nil {
t.Errorf("unexpected error %v", err)
}
}
func TestRemoveLabelNoDefinition(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.WriteTestKustomizationWith([]byte(""))
v := validators.MakeHappyMapValidator(t)
cmd := newCmdRemoveLabel(fakeFS, v.ValidatorArray)
args := []string{"label1,label2"}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err == nil {
t.Errorf("expected an error")
}
if err.Error() != "commonLabels is not defined in kustomization file" {
t.Errorf("incorrect error: %v", err.Error())
}
}
func TestRemoveLabelNoDefinitionIgnore(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.WriteTestKustomizationWith([]byte(""))
v := validators.MakeHappyMapValidator(t)
cmd := newCmdRemoveLabel(fakeFS, v.ValidatorArray)
cmd.Flag("ignore-non-existence").Value.Set("true")
args := []string{"label1,label2"}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err != nil {
t.Errorf("unexpected error %v", err)
}
}
func TestRemoveLabelNoArgs(t *testing.T) {
fakeFS := makeKustomizationFS()
v := validators.MakeHappyMapValidator(t)
cmd := newCmdRemoveLabel(fakeFS, v.ValidatorArray)
err := cmd.Execute()
v.VerifyNoCall()
if err == nil {
t.Errorf("expected an error")
}
if err.Error() != "must specify label" {
t.Errorf("incorrect error: %v", err.Error())
}
}
func TestRemoveLabelInvalidFormat(t *testing.T) {
fakeFS := makeKustomizationFS()
v := validators.MakeSadMapValidator(t)
cmd := newCmdRemoveLabel(fakeFS, v.ValidatorArray)
args := []string{"exclamation!"}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err == nil {
t.Errorf("expected an error")
}
if err.Error() != validators.SAD {
t.Errorf("incorrect error: %v", err.Error())
}
}
func TestRemoveLabelMultipleArgs(t *testing.T) {
fakeFS := makeKustomizationFS()
v := validators.MakeHappyMapValidator(t)
cmd := newCmdRemoveLabel(fakeFS, v.ValidatorArray)
args := []string{"label1,label2"}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err != nil {
t.Errorf("unexpected error %v", err)
}
m := readKustomizationFS(t, fakeFS)
splitArgs := strings.Split(args[0], ",")
for _, k := range splitArgs {
if _, exist := m.CommonLabels[k]; exist {
t.Errorf("%s must be deleted", k)
}
}
}
func TestRemoveLabelMultipleArgsInvalidFormat(t *testing.T) {
fakeFS := makeKustomizationFS()
v := validators.MakeSadMapValidator(t)
cmd := newCmdRemoveLabel(fakeFS, v.ValidatorArray)
args := []string{"label1", "label2"}
err := cmd.RunE(cmd, args)
v.VerifyNoCall()
if err == nil {
t.Errorf("expected an error")
}
if err.Error() != "labels must be comma-separated, with no spaces" {
t.Errorf("incorrect error: %v", err.Error())
}
}

View File

@@ -0,0 +1,91 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package remove
import (
"log"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/kustfile"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/util"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/patch"
"sigs.k8s.io/kustomize/v3/pkg/pgmconfig"
)
type removePatchOptions struct {
patchFilePaths []string
}
// newCmdRemovePatch removes the name of a file containing a patch from the kustomization file.
func newCmdRemovePatch(fsys fs.FileSystem) *cobra.Command {
var o removePatchOptions
cmd := &cobra.Command{
Use: "patch",
Short: "Removes one or more patches from " + pgmconfig.KustomizationFileNames[0],
Example: `
remove patch {filepath}`,
RunE: func(cmd *cobra.Command, args []string) error {
err := o.Validate(args)
if err != nil {
return err
}
err = o.Complete(cmd, args)
if err != nil {
return err
}
return o.RunRemovePatch(fsys)
},
}
return cmd
}
// Validate validates removePatch command.
func (o *removePatchOptions) Validate(args []string) error {
if len(args) == 0 {
return errors.New("must specify a patch file")
}
o.patchFilePaths = args
return nil
}
// Complete completes removePatch command.
func (o *removePatchOptions) Complete(cmd *cobra.Command, args []string) error {
return nil
}
// RunRemovePatch runs removePatch command (do real work).
func (o *removePatchOptions) RunRemovePatch(fSys fs.FileSystem) error {
patches, err := util.GlobPatterns(fSys, o.patchFilePaths)
if err != nil {
return err
}
if len(patches) == 0 {
return nil
}
mf, err := kustfile.NewKustomizationFile(fSys)
if err != nil {
return err
}
m, err := mf.Read()
if err != nil {
return err
}
var removePatches []string
for _, p := range patches {
if !patch.Exist(m.PatchesStrategicMerge, p) {
log.Printf("patch %s doesn't exist in kustomization file", p)
continue
}
removePatches = append(removePatches, p)
}
m.PatchesStrategicMerge = patch.Delete(m.PatchesStrategicMerge, removePatches...)
return mf.Write(m)
}

View File

@@ -0,0 +1,136 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package remove
import (
"fmt"
"strings"
"testing"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/patch"
)
const (
patchFileContent = `
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
`
)
func makeKustomizationPatchFS() fs.FileSystem {
fakeFS := fs.MakeFakeFS()
patches := []string{"patch1.yaml", "patch2.yaml"}
fakeFS.WriteTestKustomizationWith([]byte(
fmt.Sprintf("patchesStrategicMerge:\n - %s",
strings.Join(patches, "\n - "))))
for _, p := range patches {
fakeFS.WriteFile(p, []byte(patchFileContent))
}
fakeFS.WriteFile("patch3.yaml", []byte(patchFileContent))
return fakeFS
}
func TestRemovePatch(t *testing.T) {
fakeFS := makeKustomizationPatchFS()
cmd := newCmdRemovePatch(fakeFS)
args := []string{"patch1.yaml"}
err := cmd.RunE(cmd, args)
if err != nil {
t.Errorf("unexpected error %v", err)
}
m := readKustomizationFS(t, fakeFS)
for _, k := range args {
if patch.Exist(m.PatchesStrategicMerge, k) {
t.Errorf("%s must be deleted", k)
}
}
}
func TestRemovePatchMultipleArgs(t *testing.T) {
fakeFS := makeKustomizationPatchFS()
cmd := newCmdRemovePatch(fakeFS)
args := []string{"patch1.yaml", "patch2.yaml"}
err := cmd.RunE(cmd, args)
if err != nil {
t.Errorf("unexpected error %v", err)
}
m := readKustomizationFS(t, fakeFS)
for _, k := range args {
if patch.Exist(m.PatchesStrategicMerge, k) {
t.Errorf("%s must be deleted", k)
}
}
}
func TestRemovePatchGlob(t *testing.T) {
fakeFS := makeKustomizationPatchFS()
cmd := newCmdRemovePatch(fakeFS)
args := []string{"patch*.yaml"}
err := cmd.RunE(cmd, args)
if err != nil {
t.Errorf("unexpected error %v", err)
}
m := readKustomizationFS(t, fakeFS)
if len(m.PatchesStrategicMerge) != 0 {
t.Errorf("all patch must be deleted")
}
}
func TestRemovePatchNotDefinedInKustomization(t *testing.T) {
fakeFS := makeKustomizationPatchFS()
cmd := newCmdRemovePatch(fakeFS)
args := []string{"patch3.yaml"}
err := cmd.RunE(cmd, args)
if err != nil {
t.Errorf("unexpected error %v", err)
}
m := readKustomizationFS(t, fakeFS)
for _, k := range []string{"patch1.yaml", "patch2.yaml"} {
if !patch.Exist(m.PatchesStrategicMerge, k) {
t.Errorf("%s must exist", k)
}
}
}
func TestRemovePatchNotExist(t *testing.T) {
fakeFS := makeKustomizationPatchFS()
cmd := newCmdRemovePatch(fakeFS)
args := []string{"patch4.yaml"}
err := cmd.RunE(cmd, args)
if err != nil {
t.Errorf("unexpected error %v", err)
}
m := readKustomizationFS(t, fakeFS)
for _, k := range []string{"patch1.yaml", "patch2.yaml"} {
if !patch.Exist(m.PatchesStrategicMerge, k) {
t.Errorf("%s must exist", k)
}
}
}
func TestRemovePatchNoArgs(t *testing.T) {
fakeFS := makeKustomizationPatchFS()
cmd := newCmdRemovePatch(fakeFS)
err := cmd.RunE(cmd, nil)
if err == nil {
t.Errorf("expected an error")
}
if err.Error() != "must specify a patch file" {
t.Errorf("incorrect error: %v", err.Error())
}
}

View File

@@ -0,0 +1,110 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package remove
import (
"errors"
"path/filepath"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/kustfile"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/pgmconfig"
)
type removeResourceOptions struct {
resourceFilePaths []string
}
// newCmdRemoveResource remove the name of a file containing a resource to the kustomization file.
func newCmdRemoveResource(fsys fs.FileSystem) *cobra.Command {
var o removeResourceOptions
cmd := &cobra.Command{
Use: "resource",
Short: "Removes one or more resource file paths from " + pgmconfig.KustomizationFileNames[0],
Example: `
remove resource my-resource.yml
remove resource resource1.yml resource2.yml resource3.yml
remove resource resources/*.yml
`,
RunE: func(cmd *cobra.Command, args []string) error {
err := o.Validate(args)
if err != nil {
return err
}
err = o.Complete(cmd, args)
if err != nil {
return err
}
return o.RunRemoveResource(fsys)
},
}
return cmd
}
// Validate validates removeResource command.
func (o *removeResourceOptions) Validate(args []string) error {
if len(args) == 0 {
return errors.New("must specify a resource file")
}
o.resourceFilePaths = args
return nil
}
// Complete completes removeResource command.
func (o *removeResourceOptions) Complete(cmd *cobra.Command, args []string) error {
return nil
}
// RunRemoveResource runs Resource command (do real work).
func (o *removeResourceOptions) RunRemoveResource(fSys fs.FileSystem) error {
mf, err := kustfile.NewKustomizationFile(fSys)
if err != nil {
return err
}
m, err := mf.Read()
if err != nil {
return err
}
resources, err := globPatterns(m.Resources, o.resourceFilePaths)
if err != nil {
return err
}
if len(resources) == 0 {
return nil
}
newResources := make([]string, 0, len(m.Resources))
for _, resource := range m.Resources {
if kustfile.StringInSlice(resource, resources) {
continue
}
newResources = append(newResources, resource)
}
m.Resources = newResources
return mf.Write(m)
}
func globPatterns(resources []string, patterns []string) ([]string, error) {
var result []string
for _, pattern := range patterns {
for _, resource := range resources {
match, err := filepath.Match(pattern, resource)
if err != nil {
return nil, err
}
if !match {
continue
}
result = append(result, resource)
}
}
return result, nil
}

View File

@@ -0,0 +1,158 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package remove
import (
"errors"
"fmt"
"strings"
"testing"
"sigs.k8s.io/kustomize/v3/pkg/fs"
)
func TestRemoveResources(t *testing.T) {
type given struct {
resources []string
removeArgs []string
}
type expected struct {
resources []string
deleted []string
err error
}
testCases := []struct {
description string
given given
expected expected
}{
{
description: "remove resource",
given: given{
resources: []string{
"resource1.yaml",
"resource2.yaml",
"resource3.yaml",
},
removeArgs: []string{"resource1.yaml"},
},
expected: expected{
resources: []string{
"resource2.yaml",
"resource3.yaml",
},
deleted: []string{
"resource1.yaml",
},
},
},
{
description: "remove resources with pattern",
given: given{
resources: []string{
"foo/resource1.yaml",
"foo/resource2.yaml",
"foo/resource3.yaml",
"do/not/deleteme/please.yaml",
},
removeArgs: []string{"foo/resource*.yaml"},
},
expected: expected{
resources: []string{
"do/not/deleteme/please.yaml",
},
deleted: []string{
"foo/resource1.yaml",
"foo/resource2.yaml",
"foo/resource3.yaml",
},
},
},
{
description: "nothing found to remove",
given: given{
resources: []string{
"resource1.yaml",
"resource2.yaml",
"resource3.yaml",
},
removeArgs: []string{"foo"},
},
expected: expected{
resources: []string{
"resource2.yaml",
"resource3.yaml",
"resource1.yaml",
},
},
},
{
description: "no arguments",
given: given{},
expected: expected{
err: errors.New("must specify a resource file"),
},
},
{
description: "remove with multiple pattern arguments",
given: given{
resources: []string{
"foo/foo.yaml",
"bar/bar.yaml",
"resource3.yaml",
"do/not/deleteme/please.yaml",
},
removeArgs: []string{
"foo/*.*",
"bar/*.*",
"res*.yaml",
},
},
expected: expected{
resources: []string{
"do/not/deleteme/please.yaml",
},
deleted: []string{
"foo/foo.yaml",
"bar/bar.yaml",
"resource3.yaml",
},
},
},
}
for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.WriteTestKustomizationWith([]byte(fmt.Sprintf("resources:\n - %s", strings.Join(tc.given.resources, "\n - "))))
cmd := newCmdRemoveResource(fakeFS)
err := cmd.RunE(cmd, tc.given.removeArgs)
if err != nil && tc.expected.err == nil {
t.Errorf("unexpected cmd error: %v", err)
} else if tc.expected.err != nil {
if err.Error() != tc.expected.err.Error() {
t.Errorf("expected error did not occurred. Expected: %v. Actual: %v", tc.expected.err, err)
}
return
}
content, err := fakeFS.ReadTestKustomization()
if err != nil {
t.Errorf("unexpected read error: %v", err)
}
for _, resourceFileName := range tc.expected.resources {
if !strings.Contains(string(content), resourceFileName) {
t.Errorf("expected resource not found in kustomization file.\nResource: %s\nKustomization file:\n%s", resourceFileName, content)
}
}
for _, resourceFileName := range tc.expected.deleted {
if strings.Contains(string(content), resourceFileName) {
t.Errorf("expected deleted resource found in kustomization file. Resource: %s\nKustomization file:\n%s", resourceFileName, content)
}
}
})
}
}

View File

@@ -0,0 +1,35 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package set
import (
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/ifc"
)
// NewCmdSet returns an instance of 'set' subcommand.
func NewCmdSet(fsys fs.FileSystem, v ifc.Validator) *cobra.Command {
c := &cobra.Command{
Use: "set",
Short: "Sets the value of different fields in kustomization file.",
Long: "",
Example: `
# Sets the nameprefix field
kustomize edit set nameprefix <prefix-value>
# Sets the namesuffix field
kustomize edit set namesuffix <suffix-value>
`,
Args: cobra.MinimumNArgs(1),
}
c.AddCommand(
newCmdSetNamePrefix(fsys),
newCmdSetNameSuffix(fsys),
newCmdSetNamespace(fsys, v),
newCmdSetImage(fsys),
)
return c
}

View File

@@ -0,0 +1,73 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package set
import (
"errors"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/kustfile"
"sigs.k8s.io/kustomize/v3/pkg/fs"
)
type setNamePrefixOptions struct {
prefix string
}
// newCmdSetNamePrefix sets the value of the namePrefix field in the kustomization.
func newCmdSetNamePrefix(fsys fs.FileSystem) *cobra.Command {
var o setNamePrefixOptions
cmd := &cobra.Command{
Use: "nameprefix",
Short: "Sets the value of the namePrefix field in the kustomization file.",
Example: `
The command
set nameprefix acme-
will add the field "namePrefix: acme-" to the kustomization file if it doesn't exist,
and overwrite the value with "acme-" if the field does exist.
`,
RunE: func(cmd *cobra.Command, args []string) error {
err := o.Validate(args)
if err != nil {
return err
}
err = o.Complete(cmd, args)
if err != nil {
return err
}
return o.RunSetNamePrefix(fsys)
},
}
return cmd
}
// Validate validates setNamePrefix command.
func (o *setNamePrefixOptions) Validate(args []string) error {
if len(args) != 1 {
return errors.New("must specify exactly one prefix value")
}
// TODO: add further validation on the value.
o.prefix = args[0]
return nil
}
// Complete completes setNamePrefix command.
func (o *setNamePrefixOptions) Complete(cmd *cobra.Command, args []string) error {
return nil
}
// RunSetNamePrefix runs setNamePrefix command (does real work).
func (o *setNamePrefixOptions) RunSetNamePrefix(fSys fs.FileSystem) error {
mf, err := kustfile.NewKustomizationFile(fSys)
if err != nil {
return err
}
m, err := mf.Read()
if err != nil {
return err
}
m.NamePrefix = o.prefix
return mf.Write(m)
}

View File

@@ -0,0 +1,47 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package set
import (
"strings"
"testing"
"sigs.k8s.io/kustomize/v3/pkg/fs"
)
const (
goodPrefixValue = "acme-"
)
func TestSetNamePrefixHappyPath(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.WriteTestKustomization()
cmd := newCmdSetNamePrefix(fakeFS)
args := []string{goodPrefixValue}
err := cmd.RunE(cmd, args)
if err != nil {
t.Errorf("unexpected cmd error: %v", err)
}
content, err := fakeFS.ReadTestKustomization()
if err != nil {
t.Errorf("unexpected read error: %v", err)
}
if !strings.Contains(string(content), goodPrefixValue) {
t.Errorf("expected prefix value in kustomization file")
}
}
func TestSetNamePrefixNoArgs(t *testing.T) {
fakeFS := fs.MakeFakeFS()
cmd := newCmdSetNamePrefix(fakeFS)
err := cmd.Execute()
if err == nil {
t.Errorf("expected error: %v", err)
}
if err.Error() != "must specify exactly one prefix value" {
t.Errorf("incorrect error: %v", err.Error())
}
}

View File

@@ -0,0 +1,73 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package set
import (
"errors"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/kustfile"
"sigs.k8s.io/kustomize/v3/pkg/fs"
)
type setNameSuffixOptions struct {
suffix string
}
// newCmdSetNameSuffix sets the value of the nameSuffix field in the kustomization.
func newCmdSetNameSuffix(fsys fs.FileSystem) *cobra.Command {
var o setNameSuffixOptions
cmd := &cobra.Command{
Use: "namesuffix",
Short: "Sets the value of the nameSuffix field in the kustomization file.",
Example: `
The command
set namesuffix -- -acme
will add the field "nameSuffix: -acme" to the kustomization file if it doesn't exist,
and overwrite the value with "-acme" if the field does exist.
`,
RunE: func(cmd *cobra.Command, args []string) error {
err := o.Validate(args)
if err != nil {
return err
}
err = o.Complete(cmd, args)
if err != nil {
return err
}
return o.RunSetNameSuffix(fsys)
},
}
return cmd
}
// Validate validates setNameSuffix command.
func (o *setNameSuffixOptions) Validate(args []string) error {
if len(args) != 1 {
return errors.New("must specify exactly one suffix value")
}
// TODO: add further validation on the value.
o.suffix = args[0]
return nil
}
// Complete completes setNameSuffix command.
func (o *setNameSuffixOptions) Complete(cmd *cobra.Command, args []string) error {
return nil
}
// RunSetNameSuffix runs setNameSuffix command (does real work).
func (o *setNameSuffixOptions) RunSetNameSuffix(fSys fs.FileSystem) error {
mf, err := kustfile.NewKustomizationFile(fSys)
if err != nil {
return err
}
m, err := mf.Read()
if err != nil {
return err
}
m.NameSuffix = o.suffix
return mf.Write(m)
}

View File

@@ -0,0 +1,47 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package set
import (
"strings"
"testing"
"sigs.k8s.io/kustomize/v3/pkg/fs"
)
const (
goodSuffixValue = "-acme"
)
func TestSetNameSuffixHappyPath(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.WriteTestKustomization()
cmd := newCmdSetNameSuffix(fakeFS)
args := []string{goodSuffixValue}
err := cmd.RunE(cmd, args)
if err != nil {
t.Errorf("unexpected cmd error: %v", err)
}
content, err := fakeFS.ReadTestKustomization()
if err != nil {
t.Errorf("unexpected read error: %v", err)
}
if !strings.Contains(string(content), goodSuffixValue) {
t.Errorf("expected suffix value in kustomization file")
}
}
func TestSetNameSuffixNoArgs(t *testing.T) {
fakeFS := fs.MakeFakeFS()
cmd := newCmdSetNameSuffix(fakeFS)
err := cmd.Execute()
if err == nil {
t.Errorf("expected error: %v", err)
}
if err.Error() != "must specify exactly one suffix value" {
t.Errorf("incorrect error: %v", err.Error())
}
}

View File

@@ -0,0 +1,195 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package set
import (
"errors"
"regexp"
"sort"
"strings"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/kustfile"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/image"
)
type setImageOptions struct {
imageMap map[string]image.Image
}
var pattern = regexp.MustCompile("^(.*):([a-zA-Z0-9._-]*)$")
// errors
var (
errImageNoArgs = errors.New("no image specified")
errImageInvalidArgs = errors.New(`invalid format of image, use one of the following options:
- <image>=<newimage>:<newtag>
- <image>=<newimage>@<newtag>
- <image>=<newimage>
- <image>:<newtag>
- <image>@<digest>`)
)
const separator = "="
// newCmdSetImage sets the new names, tags or digests for images in the kustomization.
func newCmdSetImage(fsys fs.FileSystem) *cobra.Command {
var o setImageOptions
cmd := &cobra.Command{
Use: "image",
Short: `Sets images and their new names, new tags or digests in the kustomization file`,
Example: `
The command
set image postgres=eu.gcr.io/my-project/postgres:latest my-app=my-registry/my-app@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3
will add
images:
- name: postgres
newName: eu.gcr.io/my-project/postgres
newTag: latest
- digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3
name: my-app
newName: my-registry/my-app
to the kustomization file if it doesn't exist,
and overwrite the previous ones if the image name exists.
The command
set image node:8.15.0 mysql=mariadb alpine@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3
will add
images:
- name: node
newTag: 8.15.0
- name: mysql
newName: mariadb
- digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3
name: alpine
to the kustomization file if it doesn't exist,
and overwrite the previous ones if the image name exists.
`,
RunE: func(cmd *cobra.Command, args []string) error {
err := o.Validate(args)
if err != nil {
return err
}
return o.RunSetImage(fsys)
},
}
return cmd
}
type overwrite struct {
name string
digest string
tag string
}
// Validate validates setImage command.
func (o *setImageOptions) Validate(args []string) error {
if len(args) == 0 {
return errImageNoArgs
}
o.imageMap = make(map[string]image.Image)
for _, arg := range args {
img, err := parse(arg)
if err != nil {
return err
}
o.imageMap[img.Name] = img
}
return nil
}
// RunSetImage runs setImage command.
func (o *setImageOptions) RunSetImage(fSys fs.FileSystem) error {
mf, err := kustfile.NewKustomizationFile(fSys)
if err != nil {
return err
}
m, err := mf.Read()
if err != nil {
return err
}
// append only new images from kustomize file
for _, im := range m.Images {
if _, ok := o.imageMap[im.Name]; ok {
continue
}
o.imageMap[im.Name] = im
}
var images []image.Image
for _, v := range o.imageMap {
images = append(images, v)
}
sort.Slice(images, func(i, j int) bool {
return images[i].Name < images[j].Name
})
m.Images = images
return mf.Write(m)
}
func parse(arg string) (image.Image, error) {
// matches if there is an image name to overwrite
// <image>=<new-image><:|@><new-tag>
if s := strings.Split(arg, separator); len(s) == 2 {
p, err := parseOverwrite(s[1], true)
return image.Image{
Name: s[0],
NewName: p.name,
NewTag: p.tag,
Digest: p.digest,
}, err
}
// matches only for <tag|digest> overwrites
// <image><:|@><new-tag>
p, err := parseOverwrite(arg, false)
return image.Image{
Name: p.name,
NewTag: p.tag,
Digest: p.digest,
}, err
}
// parseOverwrite parses the overwrite parameters
// from the given arg into a struct
func parseOverwrite(arg string, overwriteImage bool) (overwrite, error) {
// match <image>@<digest>
if d := strings.Split(arg, "@"); len(d) > 1 {
return overwrite{
name: d[0],
digest: d[1],
}, nil
}
// match <image>:<tag>
if t := pattern.FindStringSubmatch(arg); len(t) == 3 {
return overwrite{
name: t[1],
tag: t[2],
}, nil
}
// match <image>
if len(arg) > 0 && overwriteImage {
return overwrite{
name: arg,
}, nil
}
return overwrite{}, errImageInvalidArgs
}

View File

@@ -0,0 +1,224 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package set
import (
"fmt"
"strings"
"testing"
"sigs.k8s.io/kustomize/v3/pkg/fs"
)
func TestSetImage(t *testing.T) {
type given struct {
args []string
infileImages []string
}
type expected struct {
fileOutput []string
err error
}
testCases := []struct {
description string
given given
expected expected
}{
{
given: given{
args: []string{"image1=my-image1:my-tag"},
},
expected: expected{
fileOutput: []string{
"images:",
"- name: image1",
" newName: my-image1",
" newTag: my-tag",
}},
},
{
given: given{
args: []string{"image1=my-image1@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3"},
},
expected: expected{
fileOutput: []string{
"images:",
"- digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3",
" name: image1",
" newName: my-image1",
}},
},
{
given: given{
args: []string{"image1:my-tag"},
},
expected: expected{
fileOutput: []string{
"images:",
"- name: image1",
" newTag: my-tag",
}},
},
{
given: given{
args: []string{"image1@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3"},
},
expected: expected{
fileOutput: []string{
"images:",
"- digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3",
" name: image1",
}},
},
{
description: "<image>=<image>",
given: given{
args: []string{"ngnix=localhost:5000/my-project/ngnix"},
},
expected: expected{
fileOutput: []string{
"images:",
"- name: ngnix",
" newName: localhost:5000/my-project/ngnix",
}},
},
{
given: given{
args: []string{"ngnix=localhost:5000/my-project/ngnix:dev-01"},
},
expected: expected{
fileOutput: []string{
"images:",
"- name: ngnix",
" newName: localhost:5000/my-project/ngnix",
" newTag: dev-01",
}},
},
{
description: "override file",
given: given{
args: []string{"image1=foo.bar.foo:8800/foo/image1:foo-bar"},
infileImages: []string{
"images:",
"- name: image1",
" newName: my-image1",
" newTag: my-tag",
"- name: image2",
" newName: my-image2",
" newTag: my-tag2",
},
},
expected: expected{
fileOutput: []string{
"images:",
"- name: image1",
" newName: foo.bar.foo:8800/foo/image1",
" newTag: foo-bar",
"- name: image2",
" newName: my-image2",
" newTag: my-tag2",
}},
},
{
description: "override new tag and new name with just a new tag",
given: given{
args: []string{"image1:v1"},
infileImages: []string{
"images:",
"- name: image1",
" newTag: my-tag",
},
},
expected: expected{
fileOutput: []string{
"images:",
"- name: image1",
" newTag: v1",
}},
},
{
description: "multiple args with multiple overrides",
given: given{
args: []string{
"image1=foo.bar.foo:8800/foo/image1:foo-bar",
"image2=my-image2@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3",
"image3:my-tag",
},
infileImages: []string{
"images:",
"- name: image1",
" newName: my-image1",
" newTag: my-tag1",
"- name: image2",
" newName: my-image2",
" newTag: my-tag2",
"- name: image3",
" newTag: my-tag",
},
},
expected: expected{
fileOutput: []string{
"images:",
"- name: image1",
" newName: foo.bar.foo:8800/foo/image1",
" newTag: foo-bar",
"- digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3",
" name: image2",
" newName: my-image2",
"- name: image3",
" newTag: my-tag",
}},
},
{
description: "error: no args",
expected: expected{
err: errImageNoArgs,
},
},
{
description: "error: invalid args",
given: given{
args: []string{"bad", "args"},
},
expected: expected{
err: errImageInvalidArgs,
},
},
}
for _, tc := range testCases {
t.Run(fmt.Sprintf("%s%v", tc.description, tc.given.args), func(t *testing.T) {
// arrange
fakeFS := fs.MakeFakeFS()
cmd := newCmdSetImage(fakeFS)
if len(tc.given.infileImages) > 0 {
// write file with infileImages
fakeFS.WriteTestKustomizationWith([]byte(strings.Join(tc.given.infileImages, "\n")))
} else {
// writes default kustomization file
fakeFS.WriteTestKustomization()
}
// act
err := cmd.RunE(cmd, tc.given.args)
// assert
if err != tc.expected.err {
t.Errorf("Unexpedted error from set image command. Actual: %v\nExpected: %v", err, tc.expected.err)
t.FailNow()
}
content, err := fakeFS.ReadTestKustomization()
if err != nil {
t.Errorf("unexpected read error: %v", err)
t.FailNow()
}
expectedStr := strings.Join(tc.expected.fileOutput, "\n")
if !strings.Contains(string(content), expectedStr) {
t.Errorf("unexpected image in kustomization file. \nActual:\n%s\nExpected:\n%s", content, expectedStr)
}
})
}
}

View File

@@ -0,0 +1,72 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package set
import (
"errors"
"fmt"
"strings"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands/kustfile"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/ifc"
)
type setNamespaceOptions struct {
namespace string
validator ifc.Validator
}
// newCmdSetNamespace sets the value of the namespace field in the kustomization.
func newCmdSetNamespace(fsys fs.FileSystem, v ifc.Validator) *cobra.Command {
var o setNamespaceOptions
cmd := &cobra.Command{
Use: "namespace",
Short: "Sets the value of the namespace field in the kustomization file",
Example: `
The command
set namespace staging
will add the field "namespace: staging" to the kustomization file if it doesn't exist,
and overwrite the value with "staging" if the field does exist.
`,
RunE: func(cmd *cobra.Command, args []string) error {
o.validator = v
err := o.Validate(args)
if err != nil {
return err
}
return o.RunSetNamespace(fsys)
},
}
return cmd
}
// Validate validates setNamespace command.
func (o *setNamespaceOptions) Validate(args []string) error {
if len(args) != 1 {
return errors.New("must specify exactly one namespace value")
}
ns := args[0]
if errs := o.validator.ValidateNamespace(ns); len(errs) != 0 {
return fmt.Errorf("%q is not a valid namespace name: %s", ns, strings.Join(errs, ";"))
}
o.namespace = ns
return nil
}
// RunSetNamespace runs setNamespace command (does real work).
func (o *setNamespaceOptions) RunSetNamespace(fSys fs.FileSystem) error {
mf, err := kustfile.NewKustomizationFile(fSys)
if err != nil {
return err
}
m, err := mf.Read()
if err != nil {
return err
}
m.Namespace = o.namespace
return mf.Write(m)
}

View File

@@ -0,0 +1,89 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package set
import (
"fmt"
"strings"
"testing"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/validators"
)
const (
goodNamespaceValue = "staging"
)
func TestSetNamespaceHappyPath(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.WriteTestKustomization()
cmd := newCmdSetNamespace(fakeFS, validators.MakeFakeValidator())
args := []string{goodNamespaceValue}
err := cmd.RunE(cmd, args)
if err != nil {
t.Errorf("unexpected cmd error: %v", err)
}
content, err := fakeFS.ReadTestKustomization()
if err != nil {
t.Errorf("unexpected read error: %v", err)
}
expected := []byte(fmt.Sprintf("namespace: %s", goodNamespaceValue))
if !strings.Contains(string(content), string(expected)) {
t.Errorf("expected namespace in kustomization file")
}
}
func TestSetNamespaceOverride(t *testing.T) {
fakeFS := fs.MakeFakeFS()
fakeFS.WriteTestKustomization()
cmd := newCmdSetNamespace(fakeFS, validators.MakeFakeValidator())
args := []string{goodNamespaceValue}
err := cmd.RunE(cmd, args)
if err != nil {
t.Errorf("unexpected cmd error: %v", err)
}
args = []string{"newnamespace"}
err = cmd.RunE(cmd, args)
if err != nil {
t.Errorf("unexpected cmd error: %v", err)
}
content, err := fakeFS.ReadTestKustomization()
if err != nil {
t.Errorf("unexpected read error: %v", err)
}
expected := []byte("namespace: newnamespace")
if !strings.Contains(string(content), string(expected)) {
t.Errorf("expected namespace in kustomization file %s", string(content))
}
}
func TestSetNamespaceNoArgs(t *testing.T) {
fakeFS := fs.MakeFakeFS()
cmd := newCmdSetNamespace(fakeFS, validators.MakeFakeValidator())
err := cmd.Execute()
if err == nil {
t.Errorf("expected error: %v", err)
}
if err.Error() != "must specify exactly one namespace value" {
t.Errorf("incorrect error: %v", err.Error())
}
}
func TestSetNamespaceInvalid(t *testing.T) {
fakeFS := fs.MakeFakeFS()
cmd := newCmdSetNamespace(fakeFS, validators.MakeFakeValidator())
args := []string{"/badnamespace/"}
err := cmd.RunE(cmd, args)
if err == nil {
t.Errorf("expected error: %v", err)
}
if !strings.Contains(err.Error(), "is not a valid namespace name") {
t.Errorf("unexpected error: %v", err.Error())
}
}

View File

@@ -0,0 +1,294 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package kustfile
import (
"bytes"
"errors"
"fmt"
"io"
"log"
"reflect"
"regexp"
"strings"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/pgmconfig"
"sigs.k8s.io/kustomize/v3/pkg/types"
"sigs.k8s.io/yaml"
)
var fieldMarshallingOrder = determineFieldOrder()
// determineFieldOrder returns a slice of Kustomization field
// names in the preferred order for serialization to a file.
// The field list is checked against the actual struct type
// to confirm that all fields are present, and no unknown
// fields are specified. Deprecated fields are removed from
// the list, meaning they will drop to the bottom on output
// (if present). The ordering and/or deprecation of fields
// in nested structs is not determined or considered.
func determineFieldOrder() []string {
m := make(map[string]bool)
s := reflect.ValueOf(&types.Kustomization{}).Elem()
typeOfT := s.Type()
for i := 0; i < s.NumField(); i++ {
m[string(typeOfT.Field(i).Name)] = false
}
ordered := []string{
"Resources",
"Bases",
"NamePrefix",
"NameSuffix",
"Namespace",
"Crds",
"CommonLabels",
"CommonAnnotations",
"PatchesStrategicMerge",
"PatchesJson6902",
"Patches",
"ConfigMapGenerator",
"SecretGenerator",
"GeneratorOptions",
"Vars",
"Images",
"Replicas",
"Configurations",
"Generators",
"Transformers",
"Inventory",
}
// Add deprecated fields here.
deprecated := map[string]bool{}
// Account for the inlined TypeMeta fields.
var result []string
result = append(result, "APIVersion", "Kind")
m["TypeMeta"] = true
// Make sure all these fields are recognized.
for _, n := range ordered {
if _, ok := m[n]; ok {
m[n] = true
} else {
log.Fatalf("%s is not a recognized field.", n)
}
// Keep if not deprecated.
if _, f := deprecated[n]; !f {
result = append(result, n)
}
}
return result
}
// commentedField records the comment associated with a kustomization field
// field has to be a recognized kustomization field
// comment can be empty
type commentedField struct {
field string
comment []byte
}
func (cf *commentedField) appendComment(comment []byte) {
cf.comment = append(cf.comment, comment...)
}
func squash(x [][]byte) []byte {
return bytes.Join(x, []byte(``))
}
type kustomizationFile struct {
path string
fSys fs.FileSystem
originalFields []*commentedField
}
// NewKustomizationFile returns a new instance.
func NewKustomizationFile(fSys fs.FileSystem) (*kustomizationFile, error) { // nolint
mf := &kustomizationFile{fSys: fSys}
err := mf.validate()
if err != nil {
return nil, err
}
return mf, nil
}
func (mf *kustomizationFile) validate() error {
match := 0
var path []string
for _, kfilename := range pgmconfig.KustomizationFileNames {
if mf.fSys.Exists(kfilename) {
match += 1
path = append(path, kfilename)
}
}
switch match {
case 0:
return fmt.Errorf("Missing kustomization file '%s'.\n", pgmconfig.KustomizationFileNames[0])
case 1:
mf.path = path[0]
default:
return fmt.Errorf("Found multiple kustomization file: %v\n", path)
}
if mf.fSys.IsDir(mf.path) {
return fmt.Errorf("%s should be a file", mf.path)
}
return nil
}
func (mf *kustomizationFile) Read() (*types.Kustomization, error) {
data, err := mf.fSys.ReadFile(mf.path)
if err != nil {
return nil, err
}
data = types.FixKustomizationPreUnmarshalling(data)
var k types.Kustomization
err = yaml.Unmarshal(data, &k)
if err != nil {
return nil, err
}
k.FixKustomizationPostUnmarshalling()
err = mf.parseCommentedFields(data)
if err != nil {
return nil, err
}
return &k, err
}
func (mf *kustomizationFile) Write(kustomization *types.Kustomization) error {
if kustomization == nil {
return errors.New("util: kustomization file arg is nil")
}
data, err := mf.marshal(kustomization)
if err != nil {
return err
}
return mf.fSys.WriteFile(mf.path, data)
}
// StringInSlice returns true if the string is in the slice.
func StringInSlice(str string, list []string) bool {
for _, v := range list {
if v == str {
return true
}
}
return false
}
func (mf *kustomizationFile) parseCommentedFields(content []byte) error {
buffer := bytes.NewBuffer(content)
var comments [][]byte
line, err := buffer.ReadBytes('\n')
for err == nil {
if isCommentOrBlankLine(line) {
comments = append(comments, line)
} else {
matched, field := findMatchedField(line)
if matched {
mf.originalFields = append(mf.originalFields, &commentedField{field: field, comment: squash(comments)})
comments = [][]byte{}
} else if len(comments) > 0 {
mf.originalFields[len(mf.originalFields)-1].appendComment(squash(comments))
comments = [][]byte{}
}
}
line, err = buffer.ReadBytes('\n')
}
if err != io.EOF {
return err
}
return nil
}
// marshal converts a kustomization to a byte stream.
func (mf *kustomizationFile) marshal(kustomization *types.Kustomization) ([]byte, error) {
var output []byte
for _, comment := range mf.originalFields {
output = append(output, comment.comment...)
content, err := marshalField(comment.field, kustomization)
if err != nil {
return content, err
}
output = append(output, content...)
}
for _, field := range fieldMarshallingOrder {
if mf.hasField(field) {
continue
}
content, err := marshalField(field, kustomization)
if err != nil {
return content, nil
}
output = append(output, content...)
}
return output, nil
}
func (mf *kustomizationFile) hasField(name string) bool {
for _, n := range mf.originalFields {
if n.field == name {
return true
}
}
return false
}
/*
isCommentOrBlankLine determines if a line is a comment or blank line
Return true for following lines
# This line is a comment
# This line is also a comment with several leading white spaces
(The line above is a blank line)
*/
func isCommentOrBlankLine(line []byte) bool {
s := bytes.TrimRight(bytes.TrimLeft(line, " "), "\n")
return len(s) == 0 || bytes.HasPrefix(s, []byte(`#`))
}
func findMatchedField(line []byte) (bool, string) {
for _, field := range fieldMarshallingOrder {
// (?i) is for case insensitive regexp matching
r := regexp.MustCompile("^(" + "(?i)" + field + "):")
if r.Match(line) {
return true, field
}
}
return false, ""
}
// marshalField marshal a given field of a kustomization object into yaml format.
// If the field wasn't in the original kustomization.yaml file or wasn't added,
// an empty []byte is returned.
func marshalField(field string, kustomization *types.Kustomization) ([]byte, error) {
r := reflect.ValueOf(*kustomization)
v := r.FieldByName(strings.Title(field))
if !v.IsValid() || isEmpty(v) {
return []byte{}, nil
}
k := &types.Kustomization{}
kr := reflect.ValueOf(k)
kv := kr.Elem().FieldByName(strings.Title(field))
kv.Set(v)
return yaml.Marshal(k)
}
func isEmpty(v reflect.Value) bool {
// If v is a pointer type
if v.Type().Kind() == reflect.Ptr {
return v.IsNil()
}
return v.Len() == 0
}

View File

@@ -0,0 +1,337 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package kustfile
import (
"reflect"
"strings"
"testing"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/pgmconfig"
"sigs.k8s.io/kustomize/v3/pkg/types"
)
func TestFieldOrder(t *testing.T) {
expected := []string{
"APIVersion",
"Kind",
"Resources",
"Bases",
"NamePrefix",
"NameSuffix",
"Namespace",
"Crds",
"CommonLabels",
"CommonAnnotations",
"PatchesStrategicMerge",
"PatchesJson6902",
"Patches",
"ConfigMapGenerator",
"SecretGenerator",
"GeneratorOptions",
"Vars",
"Images",
"Replicas",
"Configurations",
"Generators",
"Transformers",
"Inventory",
}
actual := determineFieldOrder()
if len(expected) != len(actual) {
t.Fatalf("Incorrect field count.")
}
for i, n := range expected {
if n != actual[i] {
t.Fatalf("Bad field order.")
}
}
}
func TestWriteAndRead(t *testing.T) {
kustomization := &types.Kustomization{
NamePrefix: "prefix",
}
fSys := fs.MakeFakeFS()
fSys.WriteTestKustomization()
mf, err := NewKustomizationFile(fSys)
if err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
if err := mf.Write(kustomization); err != nil {
t.Fatalf("Couldn't write kustomization file: %v\n", err)
}
content, err := mf.Read()
if err != nil {
t.Fatalf("Couldn't read kustomization file: %v\n", err)
}
kustomization.FixKustomizationPostUnmarshalling()
if !reflect.DeepEqual(kustomization, content) {
t.Fatal("Read kustomization is different from written kustomization")
}
}
func TestNewNotExist(t *testing.T) {
fakeFS := fs.MakeFakeFS()
_, err := NewKustomizationFile(fakeFS)
if err == nil {
t.Fatalf("expect an error")
}
contained := "Missing kustomization file"
if !strings.Contains(err.Error(), contained) {
t.Fatalf("expect an error contains %q, but got %v", contained, err)
}
_, err = NewKustomizationFile(fakeFS)
if err == nil {
t.Fatalf("expect an error")
}
if !strings.Contains(err.Error(), contained) {
t.Fatalf("expect an error contains %q, but got %v", contained, err)
}
}
func TestSecondarySuffix(t *testing.T) {
kcontent := `
configMapGenerator:
- literals:
- foo=bar
- baz=qux
name: my-configmap
`
fakeFS := fs.MakeFakeFS()
fakeFS.WriteFile(pgmconfig.KustomizationFileNames[1], []byte(kcontent))
k, err := NewKustomizationFile(fakeFS)
if err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
if k.path != pgmconfig.KustomizationFileNames[1] {
t.Fatalf("Load incorrect file path %s", k.path)
}
}
func TestPreserveComments(t *testing.T) {
kustomizationContentWithComments := []byte(
`# shem qing some comments
# This is some comment we should preserve
# don't delete it
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../namespaces
- pod.yaml
- service.yaml
# something you may want to keep
vars:
- fieldref:
fieldPath: metadata.name
name: MY_SERVICE_NAME
objref:
apiVersion: v1
kind: Service
name: my-service
# some descriptions for the patches
patchesStrategicMerge:
- service.yaml
- pod.yaml
`)
fSys := fs.MakeFakeFS()
fSys.WriteTestKustomizationWith(kustomizationContentWithComments)
mf, err := NewKustomizationFile(fSys)
if err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
kustomization, err := mf.Read()
if err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
if err = mf.Write(kustomization); err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
bytes, _ := fSys.ReadFile(mf.path)
if !reflect.DeepEqual(kustomizationContentWithComments, bytes) {
t.Fatal("written kustomization with comments is not the same as original one")
}
}
func TestPreserveCommentsWithAdjust(t *testing.T) {
kustomizationContentWithComments := []byte(`
# Some comments
# This is some comment we should preserve
# don't delete it
RESOURCES:
- ../namespaces
- pod.yaml
# See which field this comment goes into
- service.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: kustomization
# something you may want to keep
vars:
- fieldref:
fieldPath: metadata.name
name: MY_SERVICE_NAME
objref:
apiVersion: v1
kind: Service
name: my-service
# some descriptions for the patches
patchesStrategicMerge:
- service.yaml
- pod.yaml
# generator options
generatorOptions:
disableNameSuffixHash: true
`)
expected := []byte(`
# Some comments
# This is some comment we should preserve
# don't delete it
# See which field this comment goes into
resources:
- ../namespaces
- pod.yaml
- service.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: kustomization
# something you may want to keep
vars:
- fieldref:
fieldPath: metadata.name
name: MY_SERVICE_NAME
objref:
apiVersion: v1
kind: Service
name: my-service
# some descriptions for the patches
patchesStrategicMerge:
- service.yaml
- pod.yaml
# generator options
generatorOptions:
disableNameSuffixHash: true
`)
fSys := fs.MakeFakeFS()
fSys.WriteTestKustomizationWith(kustomizationContentWithComments)
mf, err := NewKustomizationFile(fSys)
if err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
kustomization, err := mf.Read()
if err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
if err = mf.Write(kustomization); err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
bytes, _ := fSys.ReadFile(mf.path)
if string(expected) != string(bytes) {
t.Fatalf(
"expected =\n%s\n\nactual =\n%s\n",
string(expected), string(bytes))
}
}
func TestFixPatchesField(t *testing.T) {
kustomizationContentWithComments := []byte(`
patches:
- patch1.yaml
- patch2.yaml
`)
expected := []byte(`
patchesStrategicMerge:
- patch1.yaml
- patch2.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
`)
fSys := fs.MakeFakeFS()
fSys.WriteTestKustomizationWith(kustomizationContentWithComments)
mf, err := NewKustomizationFile(fSys)
if err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
kustomization, err := mf.Read()
if err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
if err = mf.Write(kustomization); err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
bytes, _ := fSys.ReadFile(mf.path)
if string(expected) != string(bytes) {
t.Fatalf(
"expected =\n%s\n\nactual =\n%s\n",
string(expected), string(bytes))
}
}
func TestFixPatchesFieldForExtendedPatch(t *testing.T) {
kustomizationContentWithComments := []byte(`
patches:
- path: patch1.yaml
target:
kind: Deployment
- path: patch2.yaml
target:
kind: Service
`)
expected := []byte(`
patches:
- path: patch1.yaml
target:
kind: Deployment
- path: patch2.yaml
target:
kind: Service
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
`)
fSys := fs.MakeFakeFS()
fSys.WriteTestKustomizationWith(kustomizationContentWithComments)
mf, err := NewKustomizationFile(fSys)
if err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
kustomization, err := mf.Read()
if err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
if err = mf.Write(kustomization); err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
bytes, _ := fSys.ReadFile(mf.path)
if string(expected) != string(bytes) {
t.Fatalf(
"expected =\n%s\n\nactual =\n%s\n",
string(expected), string(bytes))
}
}

View File

@@ -0,0 +1,101 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package misc
import (
"fmt"
"path/filepath"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/transformers/config/defaultconfig"
)
// NewCmdConfig returns an instance of 'config' subcommand.
func NewCmdConfig(fsys fs.FileSystem) *cobra.Command {
c := &cobra.Command{
Use: "config",
Short: "Config Kustomize transformers",
Long: "",
Example: `
# Save the default transformer configurations to a local directory
kustomize config save -d ~/.kustomize/config
`,
Args: cobra.MinimumNArgs(1),
}
c.AddCommand(
newCmdSave(fsys),
)
return c
}
type saveOptions struct {
saveDirectory string
}
func newCmdSave(fsys fs.FileSystem) *cobra.Command {
var o saveOptions
c := &cobra.Command{
Use: "save",
Short: "Save default kustomize transformer configurations to a local directory",
Long: "",
Example: `
# Save the default transformer configurations to a local directory
save -d ~/.kustomize/config
`,
RunE: func(cmd *cobra.Command, args []string) error {
err := o.Validate()
if err != nil {
return err
}
err = o.Complete(fsys)
if err != nil {
return err
}
return o.RunSave(fsys)
},
}
c.Flags().StringVarP(
&o.saveDirectory,
"directory", "d", "",
"Directory to save the default transformer configurations")
return c
}
// Validate validates the saveOptions is not empty
func (o *saveOptions) Validate() error {
if o.saveDirectory == "" {
return fmt.Errorf("must specify one local directory to save the default transformer configurations")
}
return nil
}
// Complete creates the save directory when the directory doesn't exist
func (o *saveOptions) Complete(fsys fs.FileSystem) error {
if !fsys.Exists(o.saveDirectory) {
return fsys.MkdirAll(o.saveDirectory)
}
if fsys.IsDir(o.saveDirectory) {
return nil
}
return fmt.Errorf("%s is not a directory", o.saveDirectory)
}
// RunSave saves the default transformer configurations local directory
func (o *saveOptions) RunSave(fsys fs.FileSystem) error {
m := defaultconfig.GetDefaultFieldSpecsAsMap()
for tname, tcfg := range m {
filename := filepath.Join(o.saveDirectory, tname+".yaml")
err := fsys.WriteFile(filename, []byte(tcfg))
if err != nil {
return err
}
}
return nil
}

View File

@@ -0,0 +1,74 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package misc
import (
"fmt"
"reflect"
"strings"
"testing"
"sigs.k8s.io/kustomize/v3/pkg/fs"
)
func TestValidate(t *testing.T) {
o := saveOptions{
saveDirectory: "",
}
err := o.Validate()
if !strings.Contains(err.Error(), "must specify one local directory") {
t.Fatalf("Incorrect error %v", err)
}
o.saveDirectory = "/some/dir"
err = o.Validate()
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
}
func TestComplete(t *testing.T) {
fsys := fs.MakeFakeFS()
fsys.Mkdir("/some/dir")
fsys.WriteFile("/some/file", []byte(`some file`))
type testcase struct {
dir string
expect error
}
testcases := []testcase{
{
dir: "/some/dir",
expect: nil,
},
{
dir: "/some/dir/not/existing",
expect: nil,
},
{
dir: "/some/file",
expect: fmt.Errorf("%s is not a directory", "/some/file"),
},
}
for _, tcase := range testcases {
o := saveOptions{saveDirectory: tcase.dir}
actual := o.Complete(fsys)
if !reflect.DeepEqual(actual, tcase.expect) {
t.Fatalf("Expected %v\n but bot %v\n", tcase.expect, actual)
}
}
}
func TestRunSave(t *testing.T) {
fsys := fs.MakeFakeFS()
o := saveOptions{saveDirectory: "/some/dir"}
err := o.RunSave(fsys)
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
if !fsys.Exists("/some/dir/nameprefix.yaml") {
t.Fatal("default configurations are not successfully save.")
}
}

View File

@@ -0,0 +1,73 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package misc
import (
"fmt"
"io"
"runtime"
"github.com/spf13/cobra"
)
var (
kustomizeVersion = "unknown"
goos = runtime.GOOS
goarch = runtime.GOARCH
gitCommit = "$Format:%H$" // sha1 from git, output of $(git rev-parse HEAD)
buildDate = "1970-01-01T00:00:00Z" // build date in ISO8601 format, output of $(date -u +'%Y-%m-%dT%H:%M:%SZ')
)
// version returns the version of kustomize.
type version struct {
// KustomizeVersion is a kustomize binary version.
KustomizeVersion string `json:"kustomizeVersion"`
// GitCommit is a git commit
GitCommit string `json:"gitCommit"`
// BuildDate is a build date of the binary.
BuildDate string `json:"buildDate"`
// GoOs holds OS name.
GoOs string `json:"goOs"`
// GoArch holds architecture name.
GoArch string `json:"goArch"`
}
// getVersion returns version.
func getVersion() version {
return version{
kustomizeVersion,
gitCommit,
buildDate,
goos,
goarch,
}
}
// Print prints version.
func (v version) Print(w io.Writer, short bool) {
if short {
fmt.Fprintf(w, "%s\n", v.KustomizeVersion)
} else {
fmt.Fprintf(w, "Version: %+v\n", v)
}
}
// NewCmdVersion makes version command.
func NewCmdVersion(w io.Writer) *cobra.Command {
var short bool
versionCmd := cobra.Command{
Use: "version",
Short: "Prints the kustomize version",
Example: `kustomize version`,
Run: func(cmd *cobra.Command, args []string) {
getVersion().Print(w, short)
},
}
versionCmd.Flags().BoolVar(&short, "short", false, "print just the version number")
return &versionCmd
}

View File

@@ -0,0 +1,65 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package util
import (
"fmt"
"log"
"strings"
"sigs.k8s.io/kustomize/v3/pkg/fs"
)
// GlobPatterns accepts a slice of glob strings and returns the set of
// matching file paths.
func GlobPatterns(fsys fs.FileSystem, patterns []string) ([]string, error) {
var result []string
for _, pattern := range patterns {
files, err := fsys.Glob(pattern)
if err != nil {
return nil, err
}
if len(files) == 0 {
log.Printf("%s has no match", pattern)
continue
}
result = append(result, files...)
}
return result, nil
}
// ConvertToMap converts a slice of strings in the form of
// `key:value` into a map.
func ConvertToMap(input string, kind string) (map[string]string, error) {
result := make(map[string]string)
if input == "" {
return result, nil
}
inputs := strings.Split(input, ",")
for _, input := range inputs {
c := strings.Index(input, ":")
if c == 0 {
// key is not passed
return nil, fmt.Errorf("invalid %s: '%s' (%s)", kind, input, "need k:v pair where v may be quoted")
} else if c < 0 {
// only key passed
result[input] = ""
} else {
// both key and value passed
key := input[:c]
value := trimQuotes(input[c+1:])
result[key] = value
}
}
return result, nil
}
func trimQuotes(s string) string {
if len(s) >= 2 {
if s[0] == '"' && s[len(s)-1] == '"' {
return s[1 : len(s)-1]
}
}
return s
}

View File

@@ -0,0 +1,37 @@
package util
import (
"reflect"
"testing"
)
func TestConvertToMap(t *testing.T) {
args := "a:b,c:\"d\",e:\"f:g\",g:h:k"
expected := make(map[string]string)
expected["a"] = "b"
expected["c"] = "d"
expected["e"] = "f:g"
expected["g"] = "h:k"
result, err := ConvertToMap(args, "annotation")
if err != nil {
t.Errorf("unexpected error: %v", err.Error())
}
eq := reflect.DeepEqual(expected, result)
if !eq {
t.Errorf("Converted map does not match expected, expected: %v, result: %v\n", expected, result)
}
}
func TestConvertToMapError(t *testing.T) {
args := "a:b,c:\"d\",:f:g"
_, err := ConvertToMap(args, "annotation")
if err == nil {
t.Errorf("expected an error")
}
if err.Error() != "invalid annotation: ':f:g' (need k:v pair where v may be quoted)" {
t.Errorf("incorrect error: %v", err.Error())
}
}

17
kustomize/main.go Normal file
View File

@@ -0,0 +1,17 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package main
import (
"os"
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands"
)
func main() {
if err := commands.NewDefaultCommand().Execute(); err != nil {
os.Exit(1)
}
os.Exit(0)
}