diff --git a/docs/building-functions/on_cluster_build.md b/docs/building-functions/on_cluster_build.md index 01e10f66f2..097ac4e976 100644 --- a/docs/building-functions/on_cluster_build.md +++ b/docs/building-functions/on_cluster_build.md @@ -8,29 +8,11 @@ This guide describes how you can build a Function on Cluster with Tekton Pipelin ## Prerequisite 1. Install Tekton Pipelines on the cluster. Please refer to [Tekton Pipelines documentation](https://github.com/tektoncd/pipeline/blob/main/docs/install.md) or run the following command: ```bash -kubectl apply -f https://storage.googleapis.com/tekton-releases/pipeline/previous/v0.42.0/release.yaml +kubectl apply -f https://storage.googleapis.com/tekton-releases/pipeline/previous/v0.49.0/release.yaml ``` ## Enabling a namespace to run Function related Tekton Pipelines -In each namespace that you would like to run Pipelines and deploy a Function you need to create or install the following resources. -1. Install the Git Clone Tekton Task to fetch the Function source code: -```bash -kubectl apply -f https://raw.githubusercontent.com/tektoncd/catalog/master/task/git-clone/0.4/git-clone.yaml -``` -2. Install a Tekton Task responsible for building the Function, based on the builder preference (Buildpacks or S2I) - 1. For Buildpacks builder install the Functions Buildpacks Tekton Task: - ```bash - kubectl apply -f https://raw.githubusercontent.com/knative-sandbox/kn-plugin-func/main/pkg/pipelines/resources/tekton/task/func-buildpacks/0.1/func-buildpacks.yaml - ``` - 2. For S2I builder install the S2I task: - ```bash - kubectl apply -f https://raw.githubusercontent.com/knative-sandbox/kn-plugin-func/main/pkg/pipelines/resources/tekton/task/func-s2i/0.1/func-s2i.yaml - ``` -3. Install the `kn func` Deploy Tekton Task to be able to deploy the Function on in the Pipeline: -```bash -kubectl apply -f https://raw.githubusercontent.com/knative-sandbox/kn-plugin-func/main/pkg/pipelines/resources/tekton/task/func-deploy/0.1/func-deploy.yaml -``` -4. Add permission to deploy on Knative to `default` Service Account: (This is not needed on OpenShift) +1. Add permission to deploy on Knative to `default` Service Account: (This is not needed on OpenShift) ```bash export NAMESPACE= kubectl create clusterrolebinding $NAMESPACE:knative-serving-namespaced-admin \ @@ -88,10 +70,7 @@ Please provide credentials for image registry used by Pipeline. ```bash export NAMESPACE= kubectl delete clusterrolebinding $NAMESPACE:knative-serving-namespaced-admin -kubectl delete task.tekton.dev git-clone -kubectl delete task.tekton.dev func-buildpacks -kubectl delete task.tekton.dev func-s2i -kubectl delete task.tekton.dev func-deploy + ``` 2. Uninstall Tekton Pipelines ```bash diff --git a/go.mod b/go.mod index 4d49fb47bb..95cb3c6cf5 100644 --- a/go.mod +++ b/go.mod @@ -55,6 +55,8 @@ require ( knative.dev/serving v0.37.1-0.20230603021539-349b2d61b0e8 ) +require sigs.k8s.io/controller-runtime v0.7.2 // indirect + require ( cloud.google.com/go/compute v1.19.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect @@ -166,6 +168,8 @@ require ( github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect + github.com/manifestival/client-go-client v0.5.0 + github.com/manifestival/manifestival v0.7.2 github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.17 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect diff --git a/go.sum b/go.sum index 14266e3671..1d3b3ce480 100644 --- a/go.sum +++ b/go.sum @@ -199,6 +199,7 @@ github.com/ProtonMail/go-crypto v0.0.0-20211112122917-428f8eabeeb3/go.mod h1:z4/ github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA= github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= 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/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= @@ -213,6 +214,7 @@ github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= +github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/alecthomas/jsonschema v0.0.0-20220216202328-9eeeec9d044b h1:doCpXjVwui6HUN+xgNsNS3SZ0/jUZ68Eb+mJRNOZfog= github.com/alecthomas/jsonschema v0.0.0-20220216202328-9eeeec9d044b/go.mod h1:/n6+1/DWPltRLWL/VKyUxg6tzsl5kHUCcraimt4vr60= github.com/alecthomas/kingpin v2.2.6+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE= @@ -223,6 +225,7 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= @@ -246,6 +249,7 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= @@ -619,10 +623,13 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.0.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/evanphx/json-patch/v5 v5.2.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -656,6 +663,8 @@ github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aev github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-critic/go-critic v0.4.1/go.mod h1:7/14rZGnZbY6E38VEGk2kVhoq6itzc1E68facVDK23g= github.com/go-critic/go-critic v0.4.3/go.mod h1:j4O3D4RoIwRqlZw5jJpx0BNfXWWbpcJoKu5cYSe4YmQ= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= @@ -691,31 +700,67 @@ github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v0.3.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/zapr v0.2.0 h1:v6Ji8yBW77pva6NkJKQdHLAJKrIJKRHz0RXwPqCHSR4= +github.com/go-logr/zapr v0.2.0/go.mod h1:qhKdvif7YF5GI9NWEpyxTSSBdGmzkNguibrdCNVPunU= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= +github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= +github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= +github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= +github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= +github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= @@ -912,7 +957,9 @@ github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pf github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.4.0/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= +github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gookit/color v1.2.4/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= @@ -1139,12 +1186,19 @@ github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPK github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/manifestival/client-go-client v0.5.0 h1:LZUidASM6rwTEI40wtxYDKi+VHhDRnYT4xuYuLjExp4= +github.com/manifestival/client-go-client v0.5.0/go.mod h1:sDehep6aHdIEdUKnRSvueGf2TbQfd653Sn2picTeQqM= +github.com/manifestival/manifestival v0.6.0/go.mod h1:3Qq9cnPy7sv7FVhg2Kvw0ebb35R4OdctS4UjTjHlHm4= +github.com/manifestival/manifestival v0.7.2 h1:l4uFdWX/xQK4QcRfqGoMtBvaZeWPEuwD6hVsCwUqZY4= +github.com/manifestival/manifestival v0.7.2/go.mod h1:nl3T6HlfHCeidooWVTMI9vYNTBkQ1GdhLNb+smozbdk= github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= github.com/maratori/testpackage v1.0.1/go.mod h1:ddKdw+XG0Phzhx8BFDTKgpWP4i7MpApTE5fXSKAqwDU= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= @@ -1272,6 +1326,7 @@ github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96d github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= @@ -1285,6 +1340,7 @@ github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= @@ -1300,6 +1356,7 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= @@ -1610,6 +1667,7 @@ github.com/tektoncd/triggers v0.23.1-0.20230420080448-bf603123cc0f/go.mod h1:gMy github.com/tetafro/godot v0.3.7/go.mod h1:/7NLHhv08H1+8DNj0MElpAACw1ajsCuf3TKNQxA5S+0= github.com/tetafro/godot v0.4.2/go.mod h1:/7NLHhv08H1+8DNj0MElpAACw1ajsCuf3TKNQxA5S+0= github.com/theupdateframework/go-tuf v0.5.2 h1:habfDzTmpbzBLIFGWa2ZpVhYvFBoK0C1onC3a4zuPRA= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= github.com/timakin/bodyclose v0.0.0-20200424151742-cb6215831a94/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0= @@ -1654,6 +1712,7 @@ github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlI github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI= github.com/vbauerster/mpb/v7 v7.3.2/go.mod h1:wfxIZcOJq/bG1/lAtfzMXcOiSvbqVi/5GX5WCSi+IsA= github.com/vdemeester/k8s-pkg-credentialprovider v1.17.4/go.mod h1:inCTmtUdr5KJbreVojo06krnTgaeAz/Z7lynpPk/Q2c= +github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= @@ -1698,10 +1757,14 @@ go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.etcd.io/etcd v0.5.0-alpha.5.0.20200819165624-17cef6e3e9d5/go.mod h1:skWido08r9w6Lq/w70DO5XYIKMu4QFu1+4VsqLQuJy8= go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs= +go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= @@ -1736,16 +1799,21 @@ go.starlark.net v0.0.0-20220817180228-f738f5508c12 h1:xOBJXWGEDwU5xSDxH6macxO11U go.starlark.net v0.0.0-20220817180228-f738f5508c12/go.mod h1:VZcBMdr3cT3PnBoWunTabuSEXwVAH+ZJ5zxfs3AdASk= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.8.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= @@ -1762,10 +1830,12 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/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-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= @@ -1846,6 +1916,7 @@ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/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-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1858,6 +1929,7 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/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-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -1877,6 +1949,7 @@ golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200219183655-46282727080f/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1977,6 +2050,7 @@ golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/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-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2025,6 +2099,7 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2149,6 +2224,7 @@ golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20181219222714-6e267b5cc78e/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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190221204921-83362c3779f5/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -2165,6 +2241,7 @@ golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= @@ -2176,6 +2253,8 @@ golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -2239,6 +2318,7 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.1.0/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= @@ -2459,6 +2539,7 @@ gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76 gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= +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/warnings.v0 v0.1.1/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= @@ -2499,11 +2580,13 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.5/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.25.4 h1:3YO8J4RtmG7elEgaWMb4HgmpS2CfY1QlaOz9nwB+ZSs= k8s.io/api v0.25.4/go.mod h1:IG2+RzyPQLllQxnhzD8KQNEu4c4YvyDTpSMztf4A0OQ= +k8s.io/apiextensions-apiserver v0.19.2/go.mod h1:EYNjpqIAvNZe+svXVx9j4uBaVhTB4C94HkY3w058qcg= k8s.io/apiextensions-apiserver v0.26.5 h1:VJ946z9RjyCPn3qiz4Kus/UYjCRrdn1xUvEsJFvN5Yo= k8s.io/apiextensions-apiserver v0.26.5/go.mod h1:Olsde7ZNWnyz9rsL13iXYXmL1h7kWujtKeC3yWVCDPo= k8s.io/apimachinery v0.25.4 h1:CtXsuaitMESSu339tfhVXhQrPET+EiWnIY1rcurKnAc= k8s.io/apimachinery v0.25.4/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo= k8s.io/apiserver v0.17.4/go.mod h1:5ZDQ6Xr5MNBxyi3iUZXS84QOhZl+W7Oq2us/29c0j9I= +k8s.io/apiserver v0.19.2/go.mod h1:FreAq0bJ2vtZFj9Ago/X0oNGC51GfubKK/ViOKfVAOA= k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= @@ -2513,8 +2596,10 @@ k8s.io/client-go v0.25.4 h1:3RNRDffAkNU56M/a7gUfXaEzdhZlYhoW8dgViGy5fn8= k8s.io/client-go v0.25.4/go.mod h1:8trHCAC83XKY0wsBIpbirZU4NTUpbuhc2JnI7OruGZw= k8s.io/cloud-provider v0.17.4/go.mod h1:XEjKDzfD+b9MTLXQFlDGkk6Ho8SGMpaU8Uugx/KNK9U= k8s.io/code-generator v0.17.2/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s= +k8s.io/code-generator v0.19.2/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk= k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0= k8s.io/component-base v0.17.4/go.mod h1:5BRqHMbbQPm2kKu35v3G+CpVq4K0RJKC7TRioF0I9lE= +k8s.io/component-base v0.19.2/go.mod h1:g5LrsiTiabMLZ40AR6Hl45f088DevyGY+cCE2agEIVo= k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= @@ -2535,11 +2620,13 @@ k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/klog/v2 v2.40.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20180731170545-e3762e86a74c/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/kube-openapi v0.0.0-20200204173128-addea2498afe/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= @@ -2549,6 +2636,8 @@ k8s.io/kubernetes v1.11.10/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/legacy-cloud-providers v0.17.4/go.mod h1:FikRNoD64ECjkxO36gkDgJeiQWwyZTuBkhu+yxOc1Js= k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20200912215256-4140de9c8800/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= @@ -2580,8 +2669,11 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.9/go.mod h1:dzAXnQbTRyDlZPJX2SUPEqvnB+j7AJjtlox7PEwigU0= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/controller-runtime v0.7.2 h1:gD2JZp0bBLLuvSRYVNvox+bRCz1UUUxKDjPUCb56Ukk= +sigs.k8s.io/controller-runtime v0.7.2/go.mod h1:pJ3YBrJiAqMAZKi6UVGuE98ZrroV1p+pIhoHsMm9wdU= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= @@ -2591,6 +2683,7 @@ sigs.k8s.io/kustomize/kyaml v0.13.9 h1:Qz53EAaFFANyNgyOEJbT/yoIHygK40/ZcvU3rgry2 sigs.k8s.io/kustomize/kyaml v0.13.9/go.mod h1:QsRbD0/KcU+wdk0/L0fIp2KLnohkVzs6fQ85/nOXac4= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18= +sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= diff --git a/hack/tekton.sh b/hack/tekton.sh index b807904d3a..1b0b56fbb0 100755 --- a/hack/tekton.sh +++ b/hack/tekton.sh @@ -22,10 +22,8 @@ set -o pipefail export TERM="${TERM:-dumb}" -tekton_release="previous/v0.47.0" -git_clone_release="0.4" +tekton_release="previous/v0.49.0" namespace="${NAMESPACE:-default}" -tasks_source_path="$(dirname "$(cd "$(dirname "$0")" && pwd )")" tekton() { echo "Installing Tekton..." @@ -39,36 +37,6 @@ tekton() { --clusterrole=knative-serving-namespaced-admin --serviceaccount="${namespace}:default" } -tekton_tasks() { - echo "Creating Pipeline tasks..." - kubectl apply -f "https://raw.githubusercontent.com/tektoncd/catalog/master/task/git-clone/${git_clone_release}/git-clone.yaml" - kubectl apply -f "${tasks_source_path}/pkg/pipelines/resources/tekton/task/func-buildpacks/0.1/func-buildpacks.yaml" - kubectl apply -f "${tasks_source_path}/pkg/pipelines/resources/tekton/task/func-s2i/0.1/func-s2i.yaml" - kubectl apply -f "${tasks_source_path}/pkg/pipelines/resources/tekton/task/func-deploy/0.1/func-deploy.yaml" -} - -## Parse input parameters -# Supported parameters: -# --tasks-only - install only Tekton Tasks -tasks_only=false -if [ $# -gt 1 ] ; then - echo "Unknown parameters, use '--tasks-only' to only install Tekton Tasks" - exit 1 -fi -if [ $# -eq 1 ] ; then - if [ "$1" == "--tasks-only" ] - then - tasks_only=true - else - echo "Unknown parameter '${1}', use '--tasks-only' to only install Tekton Tasks" - exit 1 - fi -fi - -## Installation phase -if [ $tasks_only = false ] ; then - tekton -fi -tekton_tasks +tekton echo Done diff --git a/pkg/builders/builders.go b/pkg/builders/builders.go index e172834c20..81e2c13cbd 100644 --- a/pkg/builders/builders.go +++ b/pkg/builders/builders.go @@ -57,6 +57,15 @@ func (e ErrUnknownBuilder) Error() string { return fmt.Sprintf("\"%v\" is not a known builder. Available builders are %s", e.Name, e.Known) } +// ErrBuilderNotSupported +type ErrBuilderNotSupported struct { + Builder string +} + +func (e ErrBuilderNotSupported) Error() string { + return fmt.Sprintf("builder %q is not supported", e.Builder) +} + // ErrRuntimeRequired type ErrRuntimeRequired struct { Builder string diff --git a/pkg/k8s/manifestival.go b/pkg/k8s/manifestival.go new file mode 100644 index 0000000000..7909d4f84a --- /dev/null +++ b/pkg/k8s/manifestival.go @@ -0,0 +1,14 @@ +package k8s + +import ( + mfc "github.com/manifestival/client-go-client" + "github.com/manifestival/manifestival" +) + +func GetManifestivalClient() (manifestival.Client, error) { + config, err := GetClientConfig().ClientConfig() + if err != nil { + return nil, err + } + return mfc.NewClient(config) +} diff --git a/pkg/pipelines/tekton/pipelines_pac_provider.go b/pkg/pipelines/tekton/pipelines_pac_provider.go index a2c6151c5d..b5b1447418 100644 --- a/pkg/pipelines/tekton/pipelines_pac_provider.go +++ b/pkg/pipelines/tekton/pipelines_pac_provider.go @@ -35,7 +35,7 @@ func (pp *PipelinesProvider) ConfigurePAC(ctx context.Context, f fn.Function, me } if data.ConfigureLocalResources { - if err := pp.createLocalResources(ctx, f); err != nil { + if err := pp.createLocalPACResources(ctx, f); err != nil { return err } } @@ -60,13 +60,13 @@ func (pp *PipelinesProvider) ConfigurePAC(ctx context.Context, f fn.Function, me } if data.ConfigureClusterResources { - if err := pp.createClusterResources(ctx, f, data); err != nil { + if err := pp.createClusterPACResources(ctx, f, data); err != nil { return err } } if data.ConfigureRemoteResources { - if err := pp.createRemoteResources(ctx, f, data); err != nil { + if err := pp.createRemotePACResources(ctx, f, data); err != nil { return err } } @@ -102,15 +102,24 @@ func (pp *PipelinesProvider) RemovePAC(ctx context.Context, f fn.Function, metad return nil } -// createLocalResources creates necessary local resources in .tekton directory: +// createLocalPACResources creates necessary local resources in .tekton directory: // Pipeline and PipelineRun templates -func (pp *PipelinesProvider) createLocalResources(ctx context.Context, f fn.Function) error { - err := createPipelineTemplate(f) +func (pp *PipelinesProvider) createLocalPACResources(ctx context.Context, f fn.Function) error { + // let's specify labels that will be applied to every resource that is created for a Pipeline + labels, err := f.LabelsMap() + if err != nil { + return err + } + if pp.decorator != nil { + labels = pp.decorator.UpdateLabels(f, labels) + } + + err = createPipelineTemplatePAC(f, labels) if err != nil { return err } - err = createPipelineRunTemplate(f) + err = createPipelineRunTemplatePAC(f, labels) if err != nil { return err } @@ -118,10 +127,10 @@ func (pp *PipelinesProvider) createLocalResources(ctx context.Context, f fn.Func return nil } -// createClusterResources create resources on cluster, it tries to detect PAC installation, +// createClusterPACResources create resources on cluster, it tries to detect PAC installation, // creates necessary secret with image registry credentials and git credentials (access tokens, webhook secrets), // also creates PVC for the function source code -func (pp *PipelinesProvider) createClusterResources(ctx context.Context, f fn.Function, metadata pipelines.PacMetadata) error { +func (pp *PipelinesProvider) createClusterPACResources(ctx context.Context, f fn.Function, metadata pipelines.PacMetadata) error { // figure out pac installation namespace installed, _, err := pac.DetectPACInstallation(ctx, "") if !installed { @@ -183,10 +192,10 @@ func (pp *PipelinesProvider) createClusterResources(ctx context.Context, f fn.Fu return nil } -// createRemoteResources creates resources on the remote git repository +// createRemotePACResources creates resources on the remote git repository // set up a webhook with secrets, access tokens and it tries to detec PAC installation // together with PAC controller route url - needed for webhook payload trigger -func (pp *PipelinesProvider) createRemoteResources(ctx context.Context, f fn.Function, metadata pipelines.PacMetadata) error { +func (pp *PipelinesProvider) createRemotePACResources(ctx context.Context, f fn.Function, metadata pipelines.PacMetadata) error { // figure out pac installation namespace installed, installationNS, err := pac.DetectPACInstallation(ctx, "") diff --git a/pkg/pipelines/tekton/pipelines_pac_provider_test.go b/pkg/pipelines/tekton/pipelines_pac_provider_test.go index 4ff6c2ca3d..f23c8d54ac 100644 --- a/pkg/pipelines/tekton/pipelines_pac_provider_test.go +++ b/pkg/pipelines/tekton/pipelines_pac_provider_test.go @@ -52,7 +52,7 @@ func Test_createLocalResources(t *testing.T) { f.Registry = TestRegistry pp := NewPipelinesProvider() - err = pp.createLocalResources(context.Background(), f) + err = pp.createLocalPACResources(context.Background(), f) if (err != nil) != tt.wantErr { t.Errorf("pp.createLocalResources() error = %v, wantErr %v", err, tt.wantErr) return @@ -76,7 +76,7 @@ func Test_deleteAllPipelineTemplates(t *testing.T) { f.Registry = TestRegistry pp := NewPipelinesProvider() - err = pp.createLocalResources(context.Background(), f) + err = pp.createLocalPACResources(context.Background(), f) if err != nil { t.Errorf("unexpected error while running pp.createLocalResources() error = %v", err) } diff --git a/pkg/pipelines/tekton/pipelines_provider.go b/pkg/pipelines/tekton/pipelines_provider.go index f2ae77d605..9d01c7ab19 100644 --- a/pkg/pipelines/tekton/pipelines_provider.go +++ b/pkg/pipelines/tekton/pipelines_provider.go @@ -3,7 +3,7 @@ package tekton import ( "archive/tar" "context" - goErrors "errors" + "errors" "fmt" "io" "io/fs" @@ -12,6 +12,7 @@ import ( "path/filepath" "strings" "sync" + "time" "k8s.io/apimachinery/pkg/api/resource" @@ -24,7 +25,7 @@ import ( "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" pipelineClient "github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" + k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" k8slabels "k8s.io/apimachinery/pkg/labels" @@ -152,10 +153,10 @@ func (pp *PipelinesProvider) Run(ctx context.Context, f fn.Function) error { } } - _, err = client.Pipelines(pp.namespace).Create(ctx, generatePipeline(f, labels), metav1.CreateOptions{}) + err = createAndApplyPipelineTemplate(f, pp.namespace, labels) if err != nil { - if !errors.IsAlreadyExists(err) { - if errors.IsNotFound(err) { + if !k8serrors.IsAlreadyExists(err) { + if k8serrors.IsNotFound(err) { return fmt.Errorf("problem creating pipeline, missing tekton?: %v", err) } return fmt.Errorf("problem creating pipeline: %v", err) @@ -184,28 +185,37 @@ func (pp *PipelinesProvider) Run(ctx context.Context, f fn.Function) error { if f.Registry == "" { f.Registry = registry } - pr, err := client.PipelineRuns(pp.namespace).Create(ctx, generatePipelineRun(f, labels), metav1.CreateOptions{}) + + err = createAndApplyPipelineRunTemplate(f, pp.namespace, labels) if err != nil { return fmt.Errorf("problem in creating pipeline run: %v", err) } - err = pp.watchPipelineRunProgress(ctx, pr) + // we need to give k8s time to actually create the Pipeline Run + time.Sleep(1 * time.Second) + + newestPipelineRun, err := findNewestPipelineRunWithRetry(ctx, f, pp.namespace, client) + if err != nil { + return fmt.Errorf("problem in listing pipeline runs: %v", err) + } + + err = pp.watchPipelineRunProgress(ctx, newestPipelineRun) if err != nil { - if !goErrors.Is(err, context.Canceled) { + if !errors.Is(err, context.Canceled) { return fmt.Errorf("problem in watching started pipeline run: %v", err) } // TODO replace deletion with pipeline-run cancellation - _ = client.PipelineRuns(pp.namespace).Delete(context.TODO(), pr.Name, metav1.DeleteOptions{}) + _ = client.PipelineRuns(pp.namespace).Delete(context.TODO(), newestPipelineRun.Name, metav1.DeleteOptions{}) return fmt.Errorf("pipeline run cancelled: %w", context.Canceled) } - pr, err = client.PipelineRuns(pp.namespace).Get(ctx, pr.Name, metav1.GetOptions{}) + newestPipelineRun, err = client.PipelineRuns(pp.namespace).Get(ctx, newestPipelineRun.Name, metav1.GetOptions{}) if err != nil { return fmt.Errorf("problem in retriving pipeline run status: %v", err) } - if pr.Status.GetCondition(apis.ConditionSucceeded).Status == corev1.ConditionFalse { - message := getFailedPipelineRunLog(ctx, client, pr, pp.namespace) + if newestPipelineRun.Status.GetCondition(apis.ConditionSucceeded).Status == corev1.ConditionFalse { + message := getFailedPipelineRunLog(ctx, client, newestPipelineRun, pp.namespace) return fmt.Errorf("function pipeline run has failed with message: \n\n%s", message) } @@ -363,7 +373,7 @@ func (pp *PipelinesProvider) removeClusterResources(ctx context.Context, f fn.Fu go func() { defer wg.Done() err := df(ctx, pp.namespace, listOptions) - if err != nil && !errors.IsNotFound(err) && !errors.IsForbidden(err) { + if err != nil && !k8serrors.IsNotFound(err) && !k8serrors.IsForbidden(err) { errChan <- err } }() @@ -389,9 +399,9 @@ func (pp *PipelinesProvider) removeClusterResources(ctx context.Context, f fn.Fu // and prints detailed description of the currently executed Tekton Task. func (pp *PipelinesProvider) watchPipelineRunProgress(ctx context.Context, pr *v1beta1.PipelineRun) error { taskProgressMsg := map[string]string{ - taskNameFetchSources: "Fetching git repository with the function source code", - taskNameBuild: "Building function image on the cluster", - taskNameDeploy: "Deploying function to the cluster", + "fetch-sources": "Fetching git repository with the function source code", + "build": "Building function image on the cluster", + "deploy": "Deploying function to the cluster", } clients, err := NewTektonClients() @@ -471,6 +481,41 @@ func getFailedPipelineRunLog(ctx context.Context, client *pipelineClient.TektonV return message } +// findNewestPipelineRunWithRetry tries to find newest Pipeline Run for the input function +func findNewestPipelineRunWithRetry(ctx context.Context, f fn.Function, namespace string, client *pipelineClient.TektonV1beta1Client) (*v1beta1.PipelineRun, error) { + l := k8slabels.SelectorFromSet(k8slabels.Set(map[string]string{fnlabels.FunctionNameKey: f.Name})) + listOptions := metav1.ListOptions{ + LabelSelector: l.String(), + } + + var newestPipelineRun *v1beta1.PipelineRun + for attempt := 1; attempt <= 3; attempt++ { + prs, err := client.PipelineRuns(namespace).List(ctx, listOptions) + if err != nil { + return nil, fmt.Errorf("problem in listing pipeline runs: %v", err) + } + + for _, pr := range prs.Items { + currentPipelineRun := pr + if len(prs.Items) < 1 || currentPipelineRun.Status.StartTime == nil { + // Restart if StartTime is nil + break + } + + if newestPipelineRun == nil || currentPipelineRun.Status.StartTime.After(newestPipelineRun.Status.StartTime.Time) { + newestPipelineRun = ¤tPipelineRun + } + } + + // If a non-nil newestPipelineRun is found, break the retry loop + if newestPipelineRun != nil { + return newestPipelineRun, nil + } + } + + return nil, fmt.Errorf("problem in listing pipeline runs: haven't found any") +} + // allows simple mocking in unit tests, use with caution regarding concurrency var createPersistentVolumeClaim = k8s.CreatePersistentVolumeClaim @@ -483,7 +528,7 @@ func createPipelinePersistentVolumeClaim(ctx context.Context, f fn.Function, nam } } err = createPersistentVolumeClaim(ctx, getPipelinePvcName(f), namespace, labels, f.Deploy.Annotations, corev1.ReadWriteOnce, pvcs) - if err != nil && !errors.IsAlreadyExists(err) { + if err != nil && !k8serrors.IsAlreadyExists(err) { return fmt.Errorf("problem creating persistent volume claim: %v", err) } return nil diff --git a/pkg/pipelines/tekton/resources.go b/pkg/pipelines/tekton/resources.go index edf6bb08e5..583962c73a 100644 --- a/pkg/pipelines/tekton/resources.go +++ b/pkg/pipelines/tekton/resources.go @@ -4,8 +4,6 @@ import ( "context" "fmt" - pplnv1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "knative.dev/func/pkg/builders" @@ -32,196 +30,6 @@ func deletePipelineRuns(ctx context.Context, namespaceOverride string, listOptio return client.PipelineRuns(namespace).DeleteCollection(ctx, metav1.DeleteOptions{}, listOptions) } -func generatePipeline(f fn.Function, labels map[string]string) *pplnv1beta1.Pipeline { - - // ----- General properties - pipelineName := getPipelineName(f) - - params := []pplnv1beta1.ParamSpec{ - { - Name: "gitRepository", - Description: "Git repository that hosts the function project", - Default: pplnv1beta1.NewArrayOrString(f.Build.Git.URL), - }, - { - Name: "gitRevision", - Description: "Git revision to build", - }, - { - Name: "contextDir", - Description: "Path where the function project is", - Default: pplnv1beta1.NewArrayOrString(""), - }, - { - Name: "imageName", - Description: "Function image name", - }, - { - Name: "registry", - Description: "Function registry", - }, - { - Name: "builderImage", - Description: "Builder image to be used", - }, - { - Name: "buildEnvs", - Description: "Environment variables to set during build time", - Type: "array", - }, - } - - workspaces := []pplnv1beta1.PipelineWorkspaceDeclaration{ - {Name: "source-workspace", Description: "Directory where function source is located."}, - {Name: "cache-workspace", Description: "Directory where build cache is stored."}, - {Name: "dockerconfig-workspace", Description: "Directory containing image registry credentials stored in `config.json` file.", Optional: true}, - } - - var taskBuild pplnv1beta1.PipelineTask - - var tasks []pplnv1beta1.PipelineTask - var buildPreReq []string - - if f.Build.Git.URL != "" { - // If Git is set up create fetch task, - // otherwise sources have been already uploaded to workspace PVC. - buildPreReq = []string{taskNameFetchSources} - tasks = append(tasks, taskFetchSources()) - } - - if f.Build.Builder == builders.Pack { - // ----- Buildpacks related properties - taskBuild = taskBuildpacks(buildPreReq) - - } else if f.Build.Builder == builders.S2I { - // ----- S2I build related properties - - params = append(params, pplnv1beta1.ParamSpec{Name: "s2iImageScriptsUrl", Description: "URL containing the default assemble and run scripts for the builder image.", - Default: pplnv1beta1.NewArrayOrString("image:///usr/libexec/s2i")}) - - taskBuild = taskS2iBuild(buildPreReq) - } - - // ----- Pipeline definition - tasks = append(tasks, taskBuild, taskDeploy(taskNameBuild)) - - return &pplnv1beta1.Pipeline{ - ObjectMeta: metav1.ObjectMeta{ - Name: pipelineName, - Labels: labels, - Annotations: f.Deploy.Annotations, - }, - Spec: pplnv1beta1.PipelineSpec{ - Params: params, - Workspaces: workspaces, - Tasks: tasks, - }, - } -} - -func generatePipelineRun(f fn.Function, labels map[string]string) *pplnv1beta1.PipelineRun { - - revision := f.Build.Git.Revision - contextDir := f.Build.Git.ContextDir - if contextDir == "" && f.Build.Builder == builders.S2I { - // TODO(lkingland): could instead update S2I to interpret empty string - // as cwd, such that builder-specific code can be kept out of here. - contextDir = "." - } - - buildEnvs := &pplnv1beta1.ParamValue{ - Type: pplnv1beta1.ParamTypeArray, - ArrayVal: []string{}, - } - if len(f.Build.BuildEnvs) > 0 { - var envs []string - for _, e := range f.Build.BuildEnvs { - envs = append(envs, e.KeyValuePair()) - } - buildEnvs.ArrayVal = envs - } else { - // need to hack empty BuildEnvs array on Tekton v0.39.0+ - // until https://github.com/tektoncd/pipeline/issues/5149 is resolved and released - buildEnvs.ArrayVal = append(buildEnvs.ArrayVal, "=") - } - - params := []pplnv1beta1.Param{ - { - Name: "gitRepository", - Value: *pplnv1beta1.NewArrayOrString(f.Build.Git.URL), - }, - { - Name: "gitRevision", - Value: *pplnv1beta1.NewArrayOrString(revision), - }, - { - Name: "contextDir", - Value: *pplnv1beta1.NewArrayOrString(contextDir), - }, - { - Name: "imageName", - Value: *pplnv1beta1.NewArrayOrString(f.Image), - }, - { - Name: "registry", - Value: *pplnv1beta1.NewArrayOrString(f.Registry), - }, - { - Name: "builderImage", - Value: *pplnv1beta1.NewArrayOrString(getBuilderImage(f)), - }, - { - Name: "buildEnvs", - Value: *buildEnvs, - }, - } - - workspaces := []pplnv1beta1.WorkspaceBinding{ - { - Name: "source-workspace", - PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ - ClaimName: getPipelinePvcName(f), - }, - SubPath: "source", - }, - { - Name: "cache-workspace", - PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ - ClaimName: getPipelinePvcName(f), - }, - SubPath: "cache", - }, - { - Name: "dockerconfig-workspace", - Secret: &corev1.SecretVolumeSource{ - SecretName: getPipelineSecretName(f), - }, - }, - } - - if f.Build.Builder == builders.S2I { - if f.Runtime == "quarkus" { - params = append(params, pplnv1beta1.Param{Name: "s2iImageScriptsUrl", Value: *pplnv1beta1.NewArrayOrString("image:///usr/local/s2i")}) - } - } - - // ----- PipelineRun definition - return &pplnv1beta1.PipelineRun{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: fmt.Sprintf("%s-run-", getPipelineName(f)), - Labels: labels, - Annotations: f.Deploy.Annotations, - }, - Spec: pplnv1beta1.PipelineRunSpec{ - PipelineRef: &pplnv1beta1.PipelineRef{ - Name: getPipelineName(f), - }, - Params: params, - Workspaces: workspaces, - }, - } -} - // guilderImage returns the builder image to use when building the Function // with the Pack strategy if it can be calculated (the Function has a defined // language runtime. Errors are checked elsewhere, so at this level they @@ -245,6 +53,10 @@ func getPipelineName(f fn.Function) string { return fmt.Sprintf("%s-%s-%s-pipeline", f.Name, f.Build.Builder, source) } +func getPipelineRunGenerateName(f fn.Function) string { + return fmt.Sprintf("%s-run-", getPipelineName(f)) +} + func getPipelineSecretName(f fn.Function) string { return fmt.Sprintf("%s-secret", getPipelineName(f)) } diff --git a/pkg/pipelines/tekton/resources_test.go b/pkg/pipelines/tekton/resources_test.go deleted file mode 100644 index 62c3f77985..0000000000 --- a/pkg/pipelines/tekton/resources_test.go +++ /dev/null @@ -1,54 +0,0 @@ -//go:build !integration -// +build !integration - -package tekton - -import ( - "testing" - - "knative.dev/func/pkg/builders" - fn "knative.dev/func/pkg/functions" -) - -func Test_generatePipeline(t *testing.T) { - testGitRepo := "http://git-repo/git.git" - testGit := fn.Git{ - URL: testGitRepo, - } - - tests := []struct { - name string - function fn.Function - taskBuildName string - }{ - { - name: "Pack builder - use buildpacks", - function: fn.Function{Build: fn.BuildSpec{Builder: builders.Pack, Git: testGit}}, - taskBuildName: "func-buildpacks", - }, - { - name: "s2i builder - use", - function: fn.Function{Build: fn.BuildSpec{Builder: builders.S2I, Git: testGit}}, - taskBuildName: "func-s2i", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - ppl := generatePipeline(tt.function, map[string]string{}) - - for _, task := range ppl.Spec.Tasks { - // let's check what is the Task used for build task - if task.Name == taskNameBuild { - if task.TaskRef.Name != tt.taskBuildName { - t.Errorf("generatePipeline(), for builder = %q: wanted build Task = %q, got = %q", tt.function.Build.Builder, tt.taskBuildName, task.TaskRef.Name) - } - return - } - } - - // we haven't found the build Task -> fail - t.Errorf("generatePipeline(), wasn't able to find build related task named = %q", taskNameBuild) - }) - } -} diff --git a/pkg/pipelines/tekton/tasks.go b/pkg/pipelines/tekton/tasks.go deleted file mode 100644 index 9b193df7c0..0000000000 --- a/pkg/pipelines/tekton/tasks.go +++ /dev/null @@ -1,120 +0,0 @@ -package tekton - -import ( - "fmt" - - pplnv1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" -) - -const ( - taskNameFetchSources = "fetch-sources" - taskNameBuild = "build" - taskNameDeploy = "deploy" -) - -func taskFetchSources() pplnv1beta1.PipelineTask { - return pplnv1beta1.PipelineTask{ - Name: taskNameFetchSources, - TaskRef: &pplnv1beta1.TaskRef{ - Name: "git-clone", - }, - Workspaces: []pplnv1beta1.WorkspacePipelineTaskBinding{{ - Name: "output", - Workspace: "source-workspace", - }}, - Params: []pplnv1beta1.Param{ - {Name: "url", Value: *pplnv1beta1.NewArrayOrString("$(params.gitRepository)")}, - {Name: "revision", Value: *pplnv1beta1.NewArrayOrString("$(params.gitRevision)")}, - }, - } -} - -func taskBuildpacks(runAfter []string) pplnv1beta1.PipelineTask { - return pplnv1beta1.PipelineTask{ - Name: taskNameBuild, - TaskRef: &pplnv1beta1.TaskRef{ - Name: "func-buildpacks", - }, - RunAfter: runAfter, - Workspaces: []pplnv1beta1.WorkspacePipelineTaskBinding{ - { - Name: "source", - Workspace: "source-workspace", - }, - { - Name: "cache", - Workspace: "cache-workspace", - }, - { - Name: "dockerconfig", - Workspace: "dockerconfig-workspace", - }}, - Params: []pplnv1beta1.Param{ - {Name: "APP_IMAGE", Value: *pplnv1beta1.NewArrayOrString("$(params.imageName)")}, - {Name: "REGISTRY", Value: *pplnv1beta1.NewArrayOrString("$(params.registry)")}, - {Name: "SOURCE_SUBPATH", Value: *pplnv1beta1.NewArrayOrString("$(params.contextDir)")}, - {Name: "BUILDER_IMAGE", Value: *pplnv1beta1.NewArrayOrString("$(params.builderImage)")}, - {Name: "ENV_VARS", Value: pplnv1beta1.ParamValue{ - Type: pplnv1beta1.ParamTypeArray, - ArrayVal: []string{"$(params.buildEnvs[*])"}, - }}, - }, - } - -} -func taskS2iBuild(runAfter []string) pplnv1beta1.PipelineTask { - params := []pplnv1beta1.Param{ - {Name: "IMAGE", Value: *pplnv1beta1.NewArrayOrString("$(params.imageName)")}, - {Name: "REGISTRY", Value: *pplnv1beta1.NewArrayOrString("$(params.registry)")}, - {Name: "PATH_CONTEXT", Value: *pplnv1beta1.NewArrayOrString("$(params.contextDir)")}, - {Name: "BUILDER_IMAGE", Value: *pplnv1beta1.NewArrayOrString("$(params.builderImage)")}, - {Name: "ENV_VARS", Value: pplnv1beta1.ParamValue{ - Type: pplnv1beta1.ParamTypeArray, - ArrayVal: []string{"$(params.buildEnvs[*])"}, - }}, - {Name: "S2I_IMAGE_SCRIPTS_URL", Value: *pplnv1beta1.NewArrayOrString("$(params.s2iImageScriptsUrl)")}, - } - return pplnv1beta1.PipelineTask{ - Name: taskNameBuild, - TaskRef: &pplnv1beta1.TaskRef{ - Name: "func-s2i", - }, - RunAfter: runAfter, - Workspaces: []pplnv1beta1.WorkspacePipelineTaskBinding{ - { - Name: "source", - Workspace: "source-workspace", - }, - { - Name: "cache", - Workspace: "cache-workspace", - }, - { - Name: "dockerconfig", - Workspace: "dockerconfig-workspace", - }}, - Params: params, - } - -} - -func taskDeploy(runAfter string) pplnv1beta1.PipelineTask { - - params := []pplnv1beta1.Param{{Name: "path", Value: *pplnv1beta1.NewArrayOrString("$(workspaces.source.path)/$(params.contextDir)")}} - - // Deploy step that uses an image produced by builds needs explicit reference to the image - params = append(params, pplnv1beta1.Param{Name: "image", Value: *pplnv1beta1.NewArrayOrString(fmt.Sprintf("$(params.imageName)@$(tasks.%s.results.IMAGE_DIGEST)", runAfter))}) - - return pplnv1beta1.PipelineTask{ - Name: taskNameDeploy, - TaskRef: &pplnv1beta1.TaskRef{ - Name: "func-deploy", - }, - RunAfter: []string{runAfter}, - Workspaces: []pplnv1beta1.WorkspacePipelineTaskBinding{{ - Name: "source", - Workspace: "source-workspace", - }}, - Params: params, - } -} diff --git a/pkg/pipelines/tekton/templates.go b/pkg/pipelines/tekton/templates.go index db143e2c34..1e85c168f5 100644 --- a/pkg/pipelines/tekton/templates.go +++ b/pkg/pipelines/tekton/templates.go @@ -1,28 +1,111 @@ package tekton import ( + "bytes" "fmt" "os" "path" + "strings" "text/template" "github.com/AlecAivazis/survey/v2" + "github.com/manifestival/manifestival" "knative.dev/func/pkg/builders" fn "knative.dev/func/pkg/functions" + "knative.dev/func/pkg/k8s" ) const ( // Local resources properties - resourcesDirectory = ".tekton" - pipelineFileName = "pipeline.yaml" - pipelineRunFilenane = "pipeline-run.yaml" - - // Tasks references - taskGitCloneRef = "git-clone" - taskFuncS2iRef = "https://raw.githubusercontent.com/%s/pkg/pipelines/resources/tekton/task/func-s2i/0.1/func-s2i.yaml" - taskFuncBuildpacksRef = "https://raw.githubusercontent.com/%s/pkg/pipelines/resources/tekton/task/func-buildpacks/0.1/func-buildpacks.yaml" - taskFuncDeployRef = "https://raw.githubusercontent.com/%s/pkg/pipelines/resources/tekton/task/func-deploy/0.1/func-deploy.yaml" + resourcesDirectory = ".tekton" + pipelineFileName = "pipeline.yaml" + pipelineRunFilenane = "pipeline-run.yaml" + pipelineFileNamePAC = "pipeline-pac.yaml" + pipelineRunFilenamePAC = "pipeline-run-pac.yaml" + + // Tasks references for PAC PipelineRun that are defined in the annotations + taskGitCloneRef = "git-clone" + taskFuncS2iPACPipelineRunRef = "https://raw.githubusercontent.com/%s/%s/pkg/pipelines/resources/tekton/task/func-s2i/0.1/func-s2i.yaml" + taskFuncBuildpacksPACPipelineRunRef = "https://raw.githubusercontent.com/%s/%s/pkg/pipelines/resources/tekton/task/func-buildpacks/0.1/func-buildpacks.yaml" + taskFuncDeployPACPipelineRunRef = "https://raw.githubusercontent.com/%s/%s/pkg/pipelines/resources/tekton/task/func-deploy/0.1/func-deploy.yaml" + + // Following section contains references for Tasks to be used in Pipeline templates, + // there is a difference if we use PAC approach or standard Tekton approach. + // + // This can be simplified once we start consuming tasks from Tekton Hub + taskFuncBuildpacksTaskRef = `taskRef: + resolver: git + params: + - name: url + value: https://github.com/%s.git + - name: revision + value: %s + - name: pathInRepo + value: pkg/pipelines/resources/tekton/task/func-buildpacks/0.1/func-buildpacks.yaml` + taskFuncBuildpacksPACTaskRef = `taskRef: + kind: Task + name: func-buildpacks` + taskFuncS2iTaskRef = `taskRef: + resolver: git + params: + - name: url + value: https://github.com/%s.git + - name: revision + value: %s + - name: pathInRepo + value: pkg/pipelines/resources/tekton/task/func-s2i/0.1/func-s2i.yaml` + taskFuncS2iPACTaskRef = `taskRef: + kind: Task + name: func-s2i` + taskFuncDeployTaskRef = `taskRef: + resolver: git + params: + - name: url + value: https://github.com/%s.git + - name: revision + value: %s + - name: pathInRepo + value: pkg/pipelines/resources/tekton/task/func-deploy/0.1/func-deploy.yaml` + taskFuncDeployPACTaskRef = `taskRef: + kind: Task + name: func-deploy` + + // Following part holds a reference to Git Clone Task to be used in Pipeline template, + // the usage depends whether we use direct code upload or Git reference for a standard (non PAC) on-cluster build + taskGitClonePACTaskRef = `- name: fetch-sources + params: + - name: url + value: $(params.gitRepository) + - name: revision + value: $(params.gitRevision) + taskRef: + kind: Task + name: git-clone + workspaces: + - name: output + workspace: source-workspace` + // TODO fix Tekton Hub reference + taskGitCloneTaskRef = `- name: fetch-sources + params: + - name: url + value: $(params.gitRepository) + - name: revision + value: $(params.gitRevision) + taskRef: + resolver: hub + params: + - name: kind + value: task + - name: name + value: git-clone + - name: version + value: "0.4" + workspaces: + - name: output + workspace: source-workspace` + runAfterFetchSourcesRef = `runAfter: + - fetch-sources` // S2I related properties defaultS2iImageScriptsUrl = "image:///usr/libexec/s2i" @@ -32,12 +115,15 @@ const ( defaultPipelinesTargetBranch = "main" ) -var FuncRepoRef = "knative/func/main" +var ( + FuncRepoRef = "knative/func" + FuncRepoBranchRef = "main" +) type templateData struct { FunctionName string Annotations map[string]string - Labels []fn.Label + Labels map[string]string ContextDir string FunctionImage string Registry string @@ -62,30 +148,45 @@ type templateData struct { FuncS2iTaskRef string FuncDeployTaskRef string + // Reference for build task - whether it should run after fetch-sources task or not + RunAfterFetchSources string + PipelineYamlURL string // S2I related properties S2iImageScriptsUrl string } -func createPipelineTemplate(f fn.Function) error { +// createPipelineTemplatePAC creates a Pipeline template used for PAC on-cluster build +// it creates the resource in the project directory +func createPipelineTemplatePAC(f fn.Function, labels map[string]string) error { data := templateData{ - FunctionName: f.Name, - Annotations: f.Deploy.Annotations, - Labels: f.Deploy.Labels, - PipelineName: getPipelineName(f), + FunctionName: f.Name, + Annotations: f.Deploy.Annotations, + Labels: labels, + PipelineName: getPipelineName(f), + RunAfterFetchSources: runAfterFetchSourcesRef, + GitCloneTaskRef: taskGitClonePACTaskRef, + FuncBuildpacksTaskRef: taskFuncBuildpacksPACTaskRef, + FuncS2iTaskRef: taskFuncS2iPACTaskRef, + FuncDeployTaskRef: taskFuncDeployPACTaskRef, } + var template string if f.Build.Builder == builders.Pack { - return createResource(f.Root, pipelineFileName, packPipelineTemplate, data) + template = packPipelineTemplate } else if f.Build.Builder == builders.S2I { - return createResource(f.Root, pipelineFileName, s2iPipelineTemplate, data) + template = s2iPipelineTemplate + } else { + return builders.ErrBuilderNotSupported{Builder: f.Build.Builder} } - return fmt.Errorf("builder %q is not supported", f.Build.Builder) + return createResource(f.Root, pipelineFileNamePAC, template, data) } -func createPipelineRunTemplate(f fn.Function) error { +// createPipelineRunTemplatePAC creates a PipelineRun template used for PAC on-cluster build +// it creates the resource in the project directory +func createPipelineRunTemplatePAC(f fn.Function, labels map[string]string) error { contextDir := f.Build.Git.ContextDir if contextDir == "" && f.Build.Builder == builders.S2I { // TODO(lkingland): could instead update S2I to interpret empty string @@ -115,7 +216,7 @@ func createPipelineRunTemplate(f fn.Function) error { data := templateData{ FunctionName: f.Name, Annotations: f.Deploy.Annotations, - Labels: f.Deploy.Labels, + Labels: labels, ContextDir: contextDir, FunctionImage: f.Image, Registry: f.Registry, @@ -130,11 +231,11 @@ func createPipelineRunTemplate(f fn.Function) error { PipelinesTargetBranch: pipelinesTargetBranch, GitCloneTaskRef: taskGitCloneRef, - FuncBuildpacksTaskRef: fmt.Sprintf(taskFuncBuildpacksRef, FuncRepoRef), - FuncS2iTaskRef: fmt.Sprintf(taskFuncS2iRef, FuncRepoRef), - FuncDeployTaskRef: fmt.Sprintf(taskFuncDeployRef, FuncRepoRef), + FuncBuildpacksTaskRef: fmt.Sprintf(taskFuncBuildpacksPACPipelineRunRef, FuncRepoRef, FuncRepoBranchRef), + FuncS2iTaskRef: fmt.Sprintf(taskFuncS2iPACPipelineRunRef, FuncRepoRef, FuncRepoBranchRef), + FuncDeployTaskRef: fmt.Sprintf(taskFuncDeployPACPipelineRunRef, FuncRepoRef, FuncRepoBranchRef), - PipelineYamlURL: fmt.Sprintf("%s/%s", resourcesDirectory, pipelineFileName), + PipelineYamlURL: fmt.Sprintf("%s/%s", resourcesDirectory, pipelineFileNamePAC), S2iImageScriptsUrl: s2iImageScriptsUrl, @@ -142,26 +243,19 @@ func createPipelineRunTemplate(f fn.Function) error { Revision: "{{ revision }}", } + var template string if f.Build.Builder == builders.Pack { - return createResource(f.Root, pipelineRunFilenane, packRunTemplate, data) + template = packRunTemplatePAC } else if f.Build.Builder == builders.S2I { - return createResource(f.Root, pipelineRunFilenane, s2iRunTemplate, data) - } - - return fmt.Errorf("builder %q is not supported", f.Build.Builder) -} - -// deleteAllPipelineTemplates deletes all templates and pipeline resources that exists for a function -// and are stored in the .tekton directory -func deleteAllPipelineTemplates(f fn.Function) string { - err := os.RemoveAll(path.Join(f.Root, resourcesDirectory)) - if err != nil { - return fmt.Sprintf("\n %v", err) + template = s2iRunTemplatePAC + } else { + return builders.ErrBuilderNotSupported{Builder: f.Build.Builder} } - return "" + return createResource(f.Root, pipelineRunFilenamePAC, template, data) } +// createResource creates a file in the input directory from the file template and a data func createResource(projectRoot, fileName, fileTemplate string, data interface{}) error { tmpl, err := template.New(fileName).Parse(fileTemplate) if err != nil { @@ -202,3 +296,176 @@ func createResource(projectRoot, fileName, fileTemplate string, data interface{} } return err } + +// deleteAllPipelineTemplates deletes all templates and pipeline resources that exists for a function +// and are stored in the .tekton directory +func deleteAllPipelineTemplates(f fn.Function) string { + err := os.RemoveAll(path.Join(f.Root, resourcesDirectory)) + if err != nil { + return fmt.Sprintf("\n %v", err) + } + + return "" +} + +// createAndApplyPipelineTemplate creates and applies Pipeline template for a standard on-cluster build +// all resources are created on the fly, if there's a Pipeline defined in the project directory, it is used instead +func createAndApplyPipelineTemplate(f fn.Function, namespace string, labels map[string]string) error { + // If Git is set up create fetch task and reference it from build task, + // otherwise sources have been already uploaded to workspace PVC. + gitCloneTaskRef := "" + runAfterFetchSources := "" + if f.Build.Git.URL != "" { + runAfterFetchSources = runAfterFetchSourcesRef + gitCloneTaskRef = taskGitCloneTaskRef + } + + data := templateData{ + FunctionName: f.Name, + Annotations: f.Deploy.Annotations, + Labels: labels, + PipelineName: getPipelineName(f), + RunAfterFetchSources: runAfterFetchSources, + GitCloneTaskRef: gitCloneTaskRef, + FuncBuildpacksTaskRef: fmt.Sprintf(taskFuncBuildpacksTaskRef, FuncRepoRef, FuncRepoBranchRef), + FuncS2iTaskRef: fmt.Sprintf(taskFuncS2iTaskRef, FuncRepoRef, FuncRepoBranchRef), + FuncDeployTaskRef: fmt.Sprintf(taskFuncDeployTaskRef, FuncRepoRef, FuncRepoBranchRef), + } + var template string + if f.Build.Builder == builders.Pack { + template = packPipelineTemplate + } else if f.Build.Builder == builders.S2I { + template = s2iPipelineTemplate + } else { + return builders.ErrBuilderNotSupported{Builder: f.Build.Builder} + } + + return createAndApplyResource(f.Root, pipelineFileName, template, "pipeline", getPipelineName(f), namespace, data) +} + +// createAndApplyPipelineRunTemplate creates and applies PipelineRun template for a standard on-cluster build +// all resources are created on the fly, if there's a PipelineRun defined in the project directory, it is used instead +func createAndApplyPipelineRunTemplate(f fn.Function, namespace string, labels map[string]string) error { + contextDir := f.Build.Git.ContextDir + if contextDir == "" && f.Build.Builder == builders.S2I { + // TODO(lkingland): could instead update S2I to interpret empty string + // as cwd, such that builder-specific code can be kept out of here. + contextDir = "." + } + + pipelinesTargetBranch := f.Build.Git.Revision + if pipelinesTargetBranch == "" { + pipelinesTargetBranch = defaultPipelinesTargetBranch + } + + buildEnvs := []string{} + if len(f.Build.BuildEnvs) == 0 { + buildEnvs = []string{"="} + } else { + for i := range f.Build.BuildEnvs { + buildEnvs = append(buildEnvs, f.Build.BuildEnvs[i].KeyValuePair()) + } + } + + s2iImageScriptsUrl := defaultS2iImageScriptsUrl + if f.Runtime == "quarkus" { + s2iImageScriptsUrl = quarkusS2iImageScriptsUrl + } + + data := templateData{ + FunctionName: f.Name, + Annotations: f.Deploy.Annotations, + Labels: labels, + ContextDir: contextDir, + FunctionImage: f.Image, + Registry: f.Registry, + BuilderImage: getBuilderImage(f), + BuildEnvs: buildEnvs, + + PipelineName: getPipelineName(f), + PipelineRunName: getPipelineRunGenerateName(f), + PvcName: getPipelinePvcName(f), + SecretName: getPipelineSecretName(f), + + S2iImageScriptsUrl: s2iImageScriptsUrl, + + RepoUrl: f.Build.Git.URL, + Revision: pipelinesTargetBranch, + } + + var template string + if f.Build.Builder == builders.Pack { + template = packRunTemplate + } else if f.Build.Builder == builders.S2I { + template = s2iRunTemplate + } else { + return builders.ErrBuilderNotSupported{Builder: f.Build.Builder} + } + + return createAndApplyResource(f.Root, pipelineFileName, template, "pipelinerun", getPipelineRunGenerateName(f), namespace, data) +} + +// allows simple mocking in unit tests +var manifestivalClient = k8s.GetManifestivalClient + +// createAndApplyResource tries to create and apply a resource to the k8s cluster from the input template and data, +// if there's the same resource already created in the project directory, it is used instead +func createAndApplyResource(projectRoot, fileName, fileTemplate, kind, resourceName, namespace string, data interface{}) error { + var source manifestival.Source + + filePath := path.Join(projectRoot, resourcesDirectory, fileName) + if _, err := os.Stat(filePath); !os.IsNotExist(err) { + source = manifestival.Path(filePath) + } else { + tmpl, err := template.New("template").Parse(fileTemplate) + if err != nil { + return fmt.Errorf("error parsing template: %v", err) + } + + var buf bytes.Buffer + err = tmpl.Execute(&buf, data) + if err != nil { + return fmt.Errorf("error executing template: %v", err) + } + source = manifestival.Reader(&buf) + } + + client, err := manifestivalClient() + if err != nil { + return fmt.Errorf("error generating template: %v", err) + } + + m, err := manifestival.ManifestFrom(source, manifestival.UseClient(client)) + if err != nil { + return fmt.Errorf("error generating template: %v", err) + } + + resources := m.Resources() + if len(resources) != 1 { + return fmt.Errorf("error creating pipeline resources: there could be only a single resource in the template file %q", filePath) + } + + if strings.ToLower(resources[0].GetKind()) != kind { + return fmt.Errorf("error creating pipeline resources: expected resource kind in file %q is %q, but got %q", filePath, kind, resources[0].GetKind()) + } + + existingResourceName := resources[0].GetName() + if kind == "pipelinerun" { + existingResourceName = resources[0].GetGenerateName() + } + if existingResourceName != resourceName { + return fmt.Errorf("error creating pipeline resources: expected resource name in file %q is %q, but got %q", filePath, resourceName, existingResourceName) + } + + if resources[0].GetNamespace() != "" && resources[0].GetNamespace() != namespace { + return fmt.Errorf("error creating pipeline resources: expected resource namespace in file %q is %q, but got %q", filePath, namespace, resources[0].GetNamespace()) + } + + m, err = m.Transform(manifestival.InjectNamespace(namespace)) + if err != nil { + fmt.Printf("error procesing template: %v", err) + return err + } + + return m.Apply() +} diff --git a/pkg/pipelines/tekton/templates_pack.go b/pkg/pipelines/tekton/templates_pack.go index 9261406f3f..8dde6ded51 100644 --- a/pkg/pipelines/tekton/templates_pack.go +++ b/pkg/pipelines/tekton/templates_pack.go @@ -1,17 +1,18 @@ package tekton const ( + // packPipelineTemplate contains the Buildpacks template used for both Tekton standard and PAC Pipeline packPipelineTemplate = ` apiVersion: tekton.dev/v1beta1 kind: Pipeline metadata: labels: {{range $key, $value := .Labels -}} - {{$key}}: {{$value}} + "{{$key}}": "{{$value}}" {{end}} annotations: {{range $key, $value := .Annotations -}} - {{$key}}: {{$value}} + "{{$key}}": "{{$value}}" {{end}} name: {{.PipelineName}} spec: @@ -40,18 +41,7 @@ spec: name: buildEnvs type: array tasks: - - name: fetch-sources - params: - - name: url - value: $(params.gitRepository) - - name: revision - value: $(params.gitRevision) - taskRef: - kind: Task - name: git-clone - workspaces: - - name: output - workspace: source-workspace + {{.GitCloneTaskRef}} - name: build params: - name: APP_IMAGE @@ -65,11 +55,8 @@ spec: - name: ENV_VARS value: - '$(params.buildEnvs[*])' - runAfter: - - fetch-sources - taskRef: - kind: Task - name: func-buildpacks + {{.RunAfterFetchSources}} + {{.FuncBuildpacksTaskRef}} workspaces: - name: source workspace: source-workspace @@ -85,9 +72,7 @@ spec: value: $(params.imageName)@$(tasks.build.results.IMAGE_DIGEST) runAfter: - build - taskRef: - kind: Task - name: func-deploy + {{.FuncDeployTaskRef}} workspaces: - name: source workspace: source-workspace @@ -101,13 +86,64 @@ spec: optional: true ` + // packRunTemplate contains the Buildpacks template used for Tekton standard PipelineRun packRunTemplate = ` apiVersion: tekton.dev/v1beta1 kind: PipelineRun metadata: labels: {{range $key, $value := .Labels -}} - {{$key}}: {{$value}} + "{{$key}}": "{{$value}}" + {{end}} + tekton.dev/pipeline: {{.PipelineName}} + annotations: + # User defined Annotations + {{range $key, $value := .Annotations -}} + "{{$key}}": "{{$value}}" + {{end}} + generateName: {{.PipelineRunName}} +spec: + params: + - name: gitRepository + value: {{.RepoUrl}} + - name: gitRevision + value: {{.Revision}} + - name: contextDir + value: {{.ContextDir}} + - name: imageName + value: {{.FunctionImage}} + - name: registry + value: {{.Registry}} + - name: builderImage + value: {{.BuilderImage}} + - name: buildEnvs + value: + {{range .BuildEnvs -}} + - {{.}} + {{end}} + pipelineRef: + name: {{.PipelineName}} + workspaces: + - name: source-workspace + persistentVolumeClaim: + claimName: {{.PvcName}} + subPath: source + - name: cache-workspace + persistentVolumeClaim: + claimName: {{.PvcName}} + subPath: cache + - name: dockerconfig-workspace + secret: + secretName: {{.SecretName}} +` + // packRunTemplatePAC contains the Buildpacks template used for the Tekton PAC PipelineRun + packRunTemplatePAC = ` +apiVersion: tekton.dev/v1beta1 +kind: PipelineRun +metadata: + labels: + {{range $key, $value := .Labels -}} + "{{$key}}": "{{$value}}" {{end}} tekton.dev/pipeline: {{.PipelineName}} annotations: @@ -135,7 +171,7 @@ metadata: # User defined Annotations {{range $key, $value := .Annotations -}} - {{$key}}: {{$value}} + "{{$key}}": "{{$value}}" {{end}} generateName: {{.PipelineRunName}} spec: diff --git a/pkg/pipelines/tekton/templates_s2i.go b/pkg/pipelines/tekton/templates_s2i.go index 875d043a95..b78fc47a90 100644 --- a/pkg/pipelines/tekton/templates_s2i.go +++ b/pkg/pipelines/tekton/templates_s2i.go @@ -1,17 +1,18 @@ package tekton const ( + // s2iPipelineTemplate contains the S2I template used for both Tekton standard and PAC Pipeline s2iPipelineTemplate = ` apiVersion: tekton.dev/v1beta1 kind: Pipeline metadata: labels: {{range $key, $value := .Labels -}} - {{$key}}: {{$value}} + "{{$key}}": "{{$value}}" {{end}} annotations: {{range $key, $value := .Annotations -}} - {{$key}}: {{$value}} + "{{$key}}": "{{$value}}" {{end}} name: {{.PipelineName}} spec: @@ -44,18 +45,7 @@ spec: type: string default: 'image:///usr/libexec/s2i' tasks: - - name: fetch-sources - params: - - name: url - value: $(params.gitRepository) - - name: revision - value: $(params.gitRevision) - taskRef: - kind: Task - name: git-clone - workspaces: - - name: output - workspace: source-workspace + {{.GitCloneTaskRef}} - name: build params: - name: IMAGE @@ -71,11 +61,8 @@ spec: - '$(params.buildEnvs[*])' - name: S2I_IMAGE_SCRIPTS_URL value: $(params.s2iImageScriptsUrl) - runAfter: - - fetch-sources - taskRef: - kind: Task - name: func-s2i + {{.RunAfterFetchSources}} + {{.FuncS2iTaskRef}} workspaces: - name: source workspace: source-workspace @@ -91,9 +78,7 @@ spec: value: $(params.imageName)@$(tasks.build.results.IMAGE_DIGEST) runAfter: - build - taskRef: - kind: Task - name: func-deploy + {{.FuncDeployTaskRef}} workspaces: - name: source workspace: source-workspace @@ -106,14 +91,66 @@ spec: name: dockerconfig-workspace optional: true ` - + // s2iRunTemplate contains the S2I template used for Tekton standard PipelineRun s2iRunTemplate = ` apiVersion: tekton.dev/v1beta1 kind: PipelineRun metadata: labels: {{range $key, $value := .Labels -}} - {{$key}}: {{$value}} + "{{$key}}": "{{$value}}" + {{end}} + tekton.dev/pipeline: {{.PipelineName}} + annotations: + # User defined Annotations + {{range $key, $value := .Annotations -}} + "{{$key}}": "{{$value}}" + {{end}} + generateName: {{.PipelineRunName}} +spec: + params: + - name: gitRepository + value: {{.RepoUrl}} + - name: gitRevision + value: {{.Revision}} + - name: contextDir + value: {{.ContextDir}} + - name: imageName + value: {{.FunctionImage}} + - name: registry + value: {{.Registry}} + - name: builderImage + value: {{.BuilderImage}} + - name: buildEnvs + value: + {{range .BuildEnvs -}} + - {{.}} + {{end}} + - name: s2iImageScriptsUrl + value: {{.S2iImageScriptsUrl}} + pipelineRef: + name: {{.PipelineName}} + workspaces: + - name: source-workspace + persistentVolumeClaim: + claimName: {{.PvcName}} + subPath: source + - name: cache-workspace + persistentVolumeClaim: + claimName: {{.PvcName}} + subPath: cache + - name: dockerconfig-workspace + secret: + secretName: {{.SecretName}} +` + // s2iRunTemplatePAC contains the S2I template used for Tekton PAC PipelineRun + s2iRunTemplatePAC = ` +apiVersion: tekton.dev/v1beta1 +kind: PipelineRun +metadata: + labels: + {{range $key, $value := .Labels -}} + "{{$key}}": "{{$value}}" {{end}} tekton.dev/pipeline: {{.PipelineName}} annotations: @@ -141,7 +178,7 @@ metadata: # User defined Annotations {{range $key, $value := .Annotations -}} - {{$key}}: {{$value}} + "{{$key}}": "{{$value}}" {{end}} generateName: {{.PipelineRunName}} spec: diff --git a/pkg/pipelines/tekton/templates_test.go b/pkg/pipelines/tekton/templates_test.go index 7f53a684d8..1ce07f3892 100644 --- a/pkg/pipelines/tekton/templates_test.go +++ b/pkg/pipelines/tekton/templates_test.go @@ -4,6 +4,9 @@ import ( "path/filepath" "testing" + "github.com/manifestival/manifestival" + "github.com/manifestival/manifestival/fake" + "knative.dev/func/pkg/builders" fn "knative.dev/func/pkg/functions" . "knative.dev/func/pkg/testing" @@ -16,7 +19,7 @@ const ( TestRegistry = "example.com/alice" ) -func Test_createPipelineTemplate(t *testing.T) { +func Test_createPipelineTemplatePAC(t *testing.T) { tests := []struct { name string root string @@ -57,14 +60,14 @@ func Test_createPipelineTemplate(t *testing.T) { f.Image = "docker.io/alice/" + f.Name f.Registry = TestRegistry - err = createPipelineTemplate(f) + err = createPipelineTemplatePAC(f, make(map[string]string)) if (err != nil) != tt.wantErr { t.Errorf("createPipelineTemplate() error = %v, wantErr %v", err, tt.wantErr) return } - fp := filepath.Join(root, resourcesDirectory, pipelineFileName) + fp := filepath.Join(root, resourcesDirectory, pipelineFileNamePAC) exists, err := FileExists(t, fp) if err != nil { t.Fatal(err) @@ -78,7 +81,7 @@ func Test_createPipelineTemplate(t *testing.T) { } } -func Test_createPipelineRunTemplate(t *testing.T) { +func Test_createPipelineRunTemplatePAC(t *testing.T) { tests := []struct { name string root string @@ -119,14 +122,14 @@ func Test_createPipelineRunTemplate(t *testing.T) { f.Image = "docker.io/alice/" + f.Name f.Registry = TestRegistry - err = createPipelineRunTemplate(f) + err = createPipelineRunTemplatePAC(f, make(map[string]string)) if (err != nil) != tt.wantErr { t.Errorf("createPipelineRunTemplate() error = %v, wantErr %v", err, tt.wantErr) return } - fp := filepath.Join(root, resourcesDirectory, pipelineRunFilenane) + fp := filepath.Join(root, resourcesDirectory, pipelineRunFilenamePAC) exists, err := FileExists(t, fp) if err != nil { t.Fatal(err) @@ -139,3 +142,189 @@ func Test_createPipelineRunTemplate(t *testing.T) { }) } } + +// testData are used by Test_createAndApplyPipelineTemplate() and Test_createAndApplyPipelineRunTemplate() +var testData = []struct { + name string + root string + builder string + runtime string + namespace string + labels map[string]string + wantErr bool +}{ + { + name: "correct - pack & node", + root: "testdata/testCreatePipelinePackNode", + runtime: "node", + builder: builders.Pack, + namespace: "test-ns", + wantErr: false, + }, + { + name: "correct - pack & quarkus", + root: "testdata/testCreatePipelinePackQuarkus", + runtime: "quarkus", + builder: builders.Pack, + namespace: "test-ns", + wantErr: false, + }, + { + name: "correct - pack & go", + root: "testdata/testCreatePipelinePackGo", + runtime: "go", + builder: builders.Pack, + namespace: "test-ns", + wantErr: false, + }, + { + name: "correct - pack & python", + root: "testdata/testCreatePipelinePackPython", + runtime: "python", + builder: builders.Pack, + namespace: "test-ns", + wantErr: false, + }, + { + name: "correct - pack & typescript", + root: "testdata/testCreatePipelinePackTypescript", + runtime: "typescript", + builder: builders.Pack, + namespace: "test-ns", + wantErr: false, + }, + { + name: "correct - pack & springboot", + root: "testdata/testCreatePipelinePackSpringboot", + runtime: "springboot", + builder: builders.Pack, + namespace: "test-ns", + wantErr: false, + }, + { + name: "correct - pack & rust", + root: "testdata/testCreatePipelinePackRust", + runtime: "rust", + builder: builders.Pack, + namespace: "test-ns", + wantErr: false, + }, + { + name: "correct - s2i & node", + root: "testdata/testCreatePipelineS2INode", + runtime: "node", + builder: builders.S2I, + namespace: "test-ns", + wantErr: false, + }, + { + name: "correct - s2i & quarkus", + root: "testdata/testCreatePipelineS2IQuarkus", + runtime: "quarkus", + builder: builders.S2I, + namespace: "test-ns", + wantErr: false, + }, + { + name: "correct - s2i & go", + root: "testdata/testCreatePipelineS2IGo", + runtime: "go", + builder: builders.S2I, + namespace: "test-ns", + wantErr: false, + }, + { + name: "correct - s2i & python", + root: "testdata/testCreatePipelineS2IPython", + runtime: "python", + builder: builders.S2I, + namespace: "test-ns", + wantErr: false, + }, + { + name: "correct - s2i & typescript", + root: "testdata/testCreatePipelineS2ITypescript", + runtime: "typescript", + builder: builders.S2I, + namespace: "test-ns", + wantErr: false, + }, + { + name: "correct - s2i & springboot", + root: "testdata/testCreatePipelineS2ISpringboot", + runtime: "springboot", + builder: builders.S2I, + namespace: "test-ns", + wantErr: false, + }, + { + name: "correct - s2i & rust", + root: "testdata/testCreatePipelineS2IRust", + runtime: "rust", + builder: builders.S2I, + namespace: "test-ns", + wantErr: false, + }, +} + +func Test_createAndApplyPipelineTemplate(t *testing.T) { + for _, tt := range testData { + t.Run(tt.name, func(t *testing.T) { + // save current function and restore it at the end + old := manifestivalClient + defer func() { manifestivalClient = old }() + + manifestivalClient = func() (manifestival.Client, error) { + return fake.New(), nil + } + + root := tt.root + defer Using(t, root)() + + f, err := fn.NewFunction(root) + if err != nil { + t.Fatal(err) + } + + f.Build.Builder = tt.builder + f.Runtime = tt.runtime + f.Image = "docker.io/alice/" + f.Name + f.Registry = TestRegistry + + if err := createAndApplyPipelineTemplate(f, tt.namespace, tt.labels); (err != nil) != tt.wantErr { + t.Errorf("createAndApplyPipelineTemplate() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func Test_createAndApplyPipelineRunTemplate(t *testing.T) { + for _, tt := range testData { + t.Run(tt.name, func(t *testing.T) { + // save current function and restore it at the end + old := manifestivalClient + defer func() { manifestivalClient = old }() + + manifestivalClient = func() (manifestival.Client, error) { + return fake.New(), nil + } + + root := tt.root + "Run" + defer Using(t, root)() + + f, err := fn.NewFunction(root) + if err != nil { + t.Fatal(err) + } + + f.Build.Builder = tt.builder + f.Runtime = tt.runtime + f.Image = "docker.io/alice/" + f.Name + f.Registry = TestRegistry + + if err := createAndApplyPipelineRunTemplate(f, tt.namespace, tt.labels); (err != nil) != tt.wantErr { + t.Errorf("createAndApplyPipelineRunTemplate() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/third_party/VENDOR-LICENSE/github.com/manifestival/client-go-client/LICENSE b/third_party/VENDOR-LICENSE/github.com/manifestival/client-go-client/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/manifestival/client-go-client/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/third_party/VENDOR-LICENSE/github.com/manifestival/manifestival/LICENSE b/third_party/VENDOR-LICENSE/github.com/manifestival/manifestival/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/third_party/VENDOR-LICENSE/github.com/manifestival/manifestival/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/third_party/VENDOR-LICENSE/sigs.k8s.io/controller-runtime/pkg/client/apiutil/LICENSE b/third_party/VENDOR-LICENSE/sigs.k8s.io/controller-runtime/pkg/client/apiutil/LICENSE new file mode 100644 index 0000000000..8dada3edaf --- /dev/null +++ b/third_party/VENDOR-LICENSE/sigs.k8s.io/controller-runtime/pkg/client/apiutil/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/manifestival/client-go-client/.gitignore b/vendor/github.com/manifestival/client-go-client/.gitignore new file mode 100644 index 0000000000..edfc3a48fd --- /dev/null +++ b/vendor/github.com/manifestival/client-go-client/.gitignore @@ -0,0 +1,14 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +vendor/ diff --git a/vendor/github.com/manifestival/client-go-client/LICENSE b/vendor/github.com/manifestival/client-go-client/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/vendor/github.com/manifestival/client-go-client/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/manifestival/client-go-client/README.md b/vendor/github.com/manifestival/client-go-client/README.md new file mode 100644 index 0000000000..a2f6637f1e --- /dev/null +++ b/vendor/github.com/manifestival/client-go-client/README.md @@ -0,0 +1,40 @@ +[![Build Status](https://github.com/manifestival/client-go-client/workflows/Build%20and%20Test/badge.svg)](https://github.com/manifestival/client-go-client/actions) + +# client-go-client + +A [client-go](https://github.com/kubernetes/client-go) implementation +of the [Manifestival](https://github.com/manifestival/manifestival) +`Client`. + +Usage +----- + +```go +import ( + mfc "github.com/manifestival/client-go-client" + mf "github.com/manifestival/manifestival" + "k8s.io/client-go/rest" +) + +func main() { + var config *rest.Config = ... + + manifest, err := mfc.NewManifest("file.yaml", config) + if err != nil { + panic("Failed to load manifest") + } + manifest.Apply() + + // a slightly more complex example + client, _ := mfc.NewClient(config) + m, err := mf.ManifestFrom(mf.Recursive("dir/"), mf.UseClient(client)) + if err != nil { + panic("Failed to load manifest") + } + m.Apply() +} +``` + +The `NewManifest` function in this library delegates to the function +of the same name in the `manifestival` package after constructing a +`manifestival.Client` implementation from the `*rest.Config`. diff --git a/vendor/github.com/manifestival/client-go-client/client.go b/vendor/github.com/manifestival/client-go-client/client.go new file mode 100644 index 0000000000..558ed823b6 --- /dev/null +++ b/vendor/github.com/manifestival/client-go-client/client.go @@ -0,0 +1,84 @@ +package client + +import ( + "context" + mfDynamic "github.com/manifestival/client-go-client/pkg/dynamic" + mf "github.com/manifestival/manifestival" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/rest" +) + +// NewManifest returns a manifestival Manifest based on a path and config +func NewManifest(pathname string, config *rest.Config, opts ...mf.Option) (mf.Manifest, error) { + client, err := NewClient(config) + if err != nil { + return mf.Manifest{}, err + } + return mf.NewManifest(pathname, append(opts, mf.UseClient(client))...) +} + +// NewClient returns a manifestival client based on a rest config +func NewClient(config *rest.Config) (mf.Client, error) { + resourceGetter, err := mfDynamic.NewForConfig(config) + if err != nil { + return nil, err + } + return &clientGoClient{resourceGetter: resourceGetter}, nil +} + +// NewUnsafeDynamicClient returns a manifestival client based on dynamic kubernetes client +func NewUnsafeDynamicClient(client dynamic.Interface) (mf.Client, error) { + resourceGetter := mfDynamic.NewUnsafeResourceGetter(client) + return &clientGoClient{resourceGetter: resourceGetter}, nil +} + +type clientGoClient struct { + resourceGetter mfDynamic.ResourceGetter +} + +// verify implementation +var _ mf.Client = (*clientGoClient)(nil) + +func (c *clientGoClient) Create(obj *unstructured.Unstructured, options ...mf.ApplyOption) error { + resource, err := c.resourceGetter.ResourceInterface(obj) + if err != nil { + return err + } + opts := mf.ApplyWith(options) + _, err = resource.Create(context.TODO(), obj, *opts.ForCreate) + return err +} + +func (c *clientGoClient) Update(obj *unstructured.Unstructured, options ...mf.ApplyOption) error { + resource, err := c.resourceGetter.ResourceInterface(obj) + if err != nil { + return err + } + opts := mf.ApplyWith(options) + _, err = resource.Update(context.TODO(), obj, *opts.ForUpdate) + return err +} + +func (c *clientGoClient) Delete(obj *unstructured.Unstructured, options ...mf.DeleteOption) error { + resource, err := c.resourceGetter.ResourceInterface(obj) + if err != nil { + return err + } + opts := mf.DeleteWith(options) + err = resource.Delete(context.TODO(), obj.GetName(), *opts.ForDelete) + if apierrors.IsNotFound(err) && opts.IgnoreNotFound { + return nil + } + return err +} + +func (c *clientGoClient) Get(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { + resource, err := c.resourceGetter.ResourceInterface(obj) + if err != nil { + return nil, err + } + return resource.Get(context.TODO(), obj.GetName(), metav1.GetOptions{}) +} diff --git a/vendor/github.com/manifestival/client-go-client/pkg/dynamic/interface.go b/vendor/github.com/manifestival/client-go-client/pkg/dynamic/interface.go new file mode 100644 index 0000000000..21ae052145 --- /dev/null +++ b/vendor/github.com/manifestival/client-go-client/pkg/dynamic/interface.go @@ -0,0 +1,13 @@ +package dynamic + +import ( + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/client-go/dynamic" +) + +// ResourceGetter is the interface to returning a Resource +type ResourceGetter interface { + // ResourceInterface translates an Unstructured object to a ResourceInterface + // The ResourceInterface can then be used to execute k8s api calls. + ResourceInterface(obj *unstructured.Unstructured) (dynamic.ResourceInterface, error) +} diff --git a/vendor/github.com/manifestival/client-go-client/pkg/dynamic/rest_mapper_resource_interface.go b/vendor/github.com/manifestival/client-go-client/pkg/dynamic/rest_mapper_resource_interface.go new file mode 100644 index 0000000000..3360482eed --- /dev/null +++ b/vendor/github.com/manifestival/client-go-client/pkg/dynamic/rest_mapper_resource_interface.go @@ -0,0 +1,48 @@ +package dynamic + +import ( + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" +) + +type clientRestMapper struct { + client dynamic.Interface + mapper meta.RESTMapper +} + +// NewForConfig returns a ResourceGetter for a rest config. +func NewForConfig(config *rest.Config) (ResourceGetter, error) { + client, err := dynamic.NewForConfig(config) + if err != nil { + return nil, err + } + + mapper, err := apiutil.NewDynamicRESTMapper(config) + + if err != nil { + return nil, err + } + + resourceGetter := &clientRestMapper{ + client: client, + mapper: mapper, + } + return resourceGetter, nil +} + +func (cm *clientRestMapper) ResourceInterface(obj *unstructured.Unstructured) (dynamic.ResourceInterface, error) { + gvk := obj.GroupVersionKind() + mapping, err := cm.mapper.RESTMapping(gvk.GroupKind(), gvk.Version) + if err != nil { + return nil, err + } + if mapping.Scope.Name() == meta.RESTScopeNameRoot { + return cm.client.Resource(mapping.Resource), nil + } + return cm.client.Resource(mapping.Resource).Namespace(obj.GetNamespace()), nil +} + +var _ ResourceGetter = (*clientRestMapper)(nil) diff --git a/vendor/github.com/manifestival/client-go-client/pkg/dynamic/unsafe_resource_interface.go b/vendor/github.com/manifestival/client-go-client/pkg/dynamic/unsafe_resource_interface.go new file mode 100644 index 0000000000..55e512cae3 --- /dev/null +++ b/vendor/github.com/manifestival/client-go-client/pkg/dynamic/unsafe_resource_interface.go @@ -0,0 +1,27 @@ +package dynamic + +import ( + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/client-go/dynamic" +) + +type unsafeResourceInterface struct { + client dynamic.Interface +} + +// NewUnsafeResourceGetter returns a ResourceGetter based on a dynamic client. +// It is considered unsafe because it makes an assumption on the resource name +// based on the Kind. +func NewUnsafeResourceGetter(client dynamic.Interface) ResourceGetter { + return &unsafeResourceInterface{ + client: client, + } +} + +func (unsaferi *unsafeResourceInterface) ResourceInterface(obj *unstructured.Unstructured) (dynamic.ResourceInterface, error) { + plural, _ := meta.UnsafeGuessKindToResource(obj.GroupVersionKind()) + return unsaferi.client.Resource(plural).Namespace(obj.GetNamespace()), nil +} + +var _ ResourceGetter = (*unsafeResourceInterface)(nil) diff --git a/vendor/github.com/manifestival/manifestival/.gitignore b/vendor/github.com/manifestival/manifestival/.gitignore new file mode 100644 index 0000000000..31e3ac6d81 --- /dev/null +++ b/vendor/github.com/manifestival/manifestival/.gitignore @@ -0,0 +1,2 @@ +vendor/ +.idea diff --git a/vendor/github.com/manifestival/manifestival/CHANGELOG.md b/vendor/github.com/manifestival/manifestival/CHANGELOG.md new file mode 100644 index 0000000000..7a369f35ce --- /dev/null +++ b/vendor/github.com/manifestival/manifestival/CHANGELOG.md @@ -0,0 +1,296 @@ +# Changelog + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) + + +## [Unreleased] + +### Changed + +### Added + +### Removed + + +## [0.7.2] - 2022-11-08 + +### Changed + +- No longer ignoring errors resulting from invalid YAML manifests [#88](https://github.com/manifestival/manifestival/issues/88) +- No longer preventing cluster-scoped resources from being owned [#90](https://github.com/manifestival/manifestival/issues/90) +- Fixed panic when unexpected errors occur during deletion [#92](https://github.com/manifestival/manifestival/issues/92) + + +## [0.7.1] - 2022-01-19 + +### Changed + +- Bump K8s dependencies to v0.22.5 - also bumped klog (v2.40.1) & go-logr (v1.2.2) +- Fixed the lifecycle of the `manifestival` annotation to be stored in the + database rather than the `Manifest` object state. Namespaces created from + varying manifests will now be deleted correctly if the annotation is + present. [#85](https://github.com/manifestival/manifestival/issues/85) + + +## [0.7.0] - 2021-02-11 + +### Changed + +- It is no longer possible to mutate manifest resources with the + `Filter` method, since now only deep copies of each resource are + passed to each `Predicate`. The only way to change a manifest's + resources is via the `Transform` method. [#75](https://github.com/manifestival/manifestival/issues/75) +- Updated Golang version to 1.15. +- Updated Kubernetes dependencies to 1.19.7. +- Updated `github.com/evanphx/json-patch` to `v5.2.0` +- Updated `github.com/go-logr/logr` to `v0.4.0` + + +## [0.6.1] - 2020-08-19 + +### Added + +- Support for generated names: if `metadata.generateName` is set and + `metadata.name` is *not* set on any resource in a manifest, that resource will + always be _created_ when the manifest is _applied_. [#65](https://github.com/manifestival/manifestival/issues/65) +- Support for CRD apiextensions.k8s.io/v1: CRD v1/v1beta has a difference + in the specification on the conversion webhook field, which needs to be + compatible in the InjectNamespace function. + +### Changed + +- Fixed the `In` predicate to not incorporate the API version in its comparison + of manifest resources. Only Group, Kind, Namespace, and Name are used to test + for equality. [#67](https://github.com/manifestival/manifestival/issues/67) +- After experimenting with dynamically constructing `Any` and `All` + predicates, we decided to partially revert + [#56](https://github.com/manifestival/manifestival/pull/56): `Any` + and `All` no longer require at least one argument as it has become + clear that `All()` should match `Everything` and `Any()` should + match `Nothing`. [#69](https://github.com/manifestival/manifestival/issues/69) + + +## [0.6.0] - 2020-07-07 + +### Changed + +- Migrated from [dep](https://github.com/golang/dep) to [go + modules](https://blog.golang.org/using-go-modules) + [#47](https://github.com/manifestival/manifestival/pull/47) +- Restored `FieldManager` option for creates and updates, essentially + reverting [#17](https://github.com/manifestival/manifestival/issues/17). + [#26](https://github.com/manifestival/manifestival/issues/26) +- Fixed the `InjectNamespace` transformer to properly update the + `spec.conversion` field in a `CustomResourceDefinition` + [#55](https://github.com/manifestival/manifestival/issues/55) +- Predicate changes: `None` was removed and replaced with `Not`, which + only accepts a single predicate. `Any` and `All` now require at + least one predicate since it wasn't clear how they should behave + without one. [#56](https://github.com/manifestival/manifestival/pull/56) +- Fixed bug where manifestival wasn't deleting namespaces it created. + (It should never delete a namespace it didn't create) + [#61](https://github.com/manifestival/manifestival/issues/61) + +### Added + +- Introduced `Append` to the `Manifestival` interface. This enables + the creation of new manifests from the concatenation of others. The + resulting manifest retains the options, e.g. client and logger, of + the receiver. [#41](https://github.com/manifestival/manifestival/issues/41) +- New fake `Client` to facilitate testing. Provides both a simple + in-memory object store and easily-override-able stubs for all the + `Client` functions: `Create`, `Update`, `Delete`, or `Get` + [#43](https://github.com/manifestival/manifestival/pull/43) +- More [docs](README.md), including + [godoc](https://godoc.org/github.com/manifestival/manifestival) + [#42](https://github.com/manifestival/manifestival/pull/42) +- New filter `Predicate`, `In`, that returns true if a resource is in + a given manifest, uniquely identified by GVK, namespace, and name + [#50](https://github.com/manifestival/manifestival/pull/50) +- New filter `Predicate`, `ByAnnotation`, that does for annotations + what `ByLabel` did for labels! + [#52](https://github.com/manifestival/manifestival/pull/52) +- Defaulting the `FieldManager` for create/updates to "manifestival" + to help reconcile changes in `metadata.managedFields`, in + anticipation of server-side apply. [#64](https://github.com/manifestival/manifestival/pull/64) + +### Removed + +- Removed dependency on `k8s.io/kubernetes`. It was only used in a + test, to verify a proper response to server-side validation errors, + but 'go modules' doesn't distinguish test-only dependencies, and + `k8s.io/kubernetes` was never intended to be consumed as a module, + so we replicated the validation logic in the test itself. + + +## [0.5.0] - 2020-03-31 + +### Changed + +- Renamed the `Replace` option to `Overwrite` to better match the + behavior of the `kubectl apply` subcommand. Its default value is now + true, which will cause `Apply` to "automatically resolve conflicts + between the modified and live configuration by using values from the + modified configuration". To override this behavior and have invalid + patches return an error, call `Apply(Overwrite(false))` [#39](https://github.com/manifestival/manifestival/pull/39) +- Made the `None` filter variadic, accepting multiple `Predicates`, + returning only those resources matching none of them. [#36](https://github.com/manifestival/manifestival/issues/36) + + +## [0.4.0] - 2020-03-11 + +### Added + +- New `DryRun` function shows how applying the manifest will change + the cluster. Its return value is a list of strategic merge patches + [#29](https://github.com/manifestival/manifestival/pull/29) +- New `ByLabels` function which is similar to `ByLabel`, but it + accepts multiple labels in a map and filters all resources that + match any of them [#32](https://github.com/manifestival/manifestival/pull/32) + +### Changed + +- Reordered/renamed parameters in the `patch` package to be more + consistent with its upstream functions. +- The `Patch` interface is now a struct +- Renamed `Patch.Apply` to `Patch.Merge` +- `Delete` may now return the errors that it was previously only + logging. It will still ignore `NotFound` errors when the + `IgnoreNotFound` option is true, of course. +- Fixed bug when transformers use `scheme.Scheme.Convert` to + manipulate resources [#33](https://github.com/manifestival/manifestival/pull/33) + + +## [0.3.1] - 2020-02-26 + +### Changed + +- Bugfix: set LastAppliedConfigAnnotation correctly on updates + [#27](https://github.com/manifestival/manifestival/issues/27) + + +## [0.3.0] - 2020-02-25 + +### Added + +- Introduced `All` and `Any` predicates, implementing `Filter` in + terms of the former +- A new `ApplyOption` called `Replace` that defaults to false. Can be + used to force a replace update instead of a merge patch when + applying a manifest, e.g. `m.Apply(Replace(true))` or + `m.Apply(ForceReplace)` [#23](https://github.com/manifestival/manifestival/issues/23) + +### Removed + +- `ConfigMaps` are no longer handled specially when applying manifests + +### Changed + +- Renamed `Complement` to `None`, `JustCRDs` to `CRDs`, and `NotCRDs` + to `NoCRDs`. + + +## [0.2.0] - 2020-02-21 + +### Added + +- Introduced the `Source` interface, enabling the creation of a + Manifest from any source [#11](https://github.com/manifestival/manifestival/pull/11) +- Added a `ManifestFrom` constructor to complement `NewManifest`, + which now only works for paths to files, directories, and URL's +- Use `ManifestFrom(Recursive("dirname/"))` to create a manifest from + a recursive directory search for yaml files. +- Use `ManifestFrom(Slice(v))` to create a manifest from any `v` of type + `[]unstructured.Unstructured` +- Use `ManifestFrom(Reader(r))` to create a manifest from any `r` of + type `io.Reader` +- Introduced a new `Filter` function in the `Manifestival` interface + that returns a subset of resources matching one or more `Predicates` +- Convenient predicates provided: `ByName`, `ByKind`, `ByLabel`, + `ByGVK`, `Complement`, `JustCRDs`, and `NotCRDs` + +### Removed + +- In order to support k8s versions <1.14, the code no longer + references the `FieldManager` field of either `metav1.CreateOptions` + or `metav1.UpdateOptions`. This only affects `client-go` clients who + set `FieldManager` on their creates/updates; `controller-runtime` + clients aren't affected. + [#17](https://github.com/manifestival/manifestival/issues/17) + +### Changed + +- Removed the "convenience" functions from the `Manifestival` + interface and renamed `ApplyAll` and `DeleteAll` to `Apply` and + `Delete`, respectively. [#14](https://github.com/manifestival/manifestival/issues/14) +- The `Manifest` struct's `Client` is now public, so the handy `Get` + and `Delete` functions formerly in the `Manifestival` interface can + now be invoked directly on any manifest's `Client` member. +- Manifests created from a recursive directory search are now only + possible via the new `ManifestFrom` constructor. The `NewManifest` + constructor no longer supports a `recursive` option. +- Moved the path/yaml parsing logic into its own `sources` package to + reduce the exported names in the `manifestival` package. +- Split the `ClientOption` type into `ApplyOption` and `DeleteOption`, + adding `IgnoreNotFound` to the latter, thereby enabling `Client` + implementations to honor it, simplifying delete logic for users + invoking the `Client` directly. All `ApplyOptions` apply to both + creates and updates + [#12](https://github.com/manifestival/manifestival/pull/12) +- The `Manifest` struct's `Resources` member is no longer public. + Instead, a `Manifest.Resources()` function is provided to return a + deep copy of the manifest's resources, if needed. +- `Transform` now returns a `Manifest` by value, like `Filter`. This + is a stronger indicator of immutability and conveniently matches the + return type of `NewManifest`. [#18](https://github.com/manifestival/manifestival/issues/18) +- The receiver types for the `Apply`, `Delete`, and `Resources` + methods on `Manifest` are now values, enabling convenient chaining + of calls involving `Filter`, for example. [#21](https://github.com/manifestival/manifestival/issues/21) + + +## [0.1.0] - 2020-02-17 + +### Changed + +- Factored client API calls into a `Client` interface; implementations + for [controller-runtime] and [client-go] reside in separate repos + within this org [#4](https://github.com/manifestival/manifestival/issues/4) +- Introduced `Option` and `ClientOption` types, enabling golang's + "functional options" pattern for both Manifest creation and `Client` + interface options, respectively [#6](https://github.com/manifestival/manifestival/issues/6) +- Transforms are now immutable, a feature developed in the old + `client-go` branch +- Except for `ConfigMaps`, manifest resources are now applied using + kubectl's strategic 3-way merging, a feature also developed in the + old `client-go` branch. `ConfigMaps` use a JSON merge patch, + which is essentially a "replace" +- Other small fixes from the old `client-go` branch have also been + merged + + +## [0.0.0] - 2020-01-11 + +### Changed + +- This release represents the move from this project's original home, + https://github.com/jcrossley3/manifestival. +- There were no releases in the old repo, just two branches: `master`, + based on the `controller-runtime` client API, and `client-go`, based + on the `client-go` API. + + +[controller-runtime]: https://github.com/manifestival/controller-runtime-client +[client-go]: https://github.com/manifestival/client-go-client +[Unreleased]: https://github.com/manifestival/manifestival/compare/v0.7.2...HEAD +[0.7.2]: https://github.com/manifestival/manifestival/compare/v0.7.1...v0.7.2 +[0.7.1]: https://github.com/manifestival/manifestival/compare/v0.7.0...v0.7.1 +[0.7.0]: https://github.com/manifestival/manifestival/compare/v0.6.1...v0.7.0 +[0.6.1]: https://github.com/manifestival/manifestival/compare/v0.6.0...v0.6.1 +[0.6.0]: https://github.com/manifestival/manifestival/compare/v0.5.0...v0.6.0 +[0.5.0]: https://github.com/manifestival/manifestival/compare/v0.4.0...v0.5.0 +[0.4.0]: https://github.com/manifestival/manifestival/compare/v0.3.1...v0.4.0 +[0.3.1]: https://github.com/manifestival/manifestival/compare/v0.3.0...v0.3.1 +[0.3.0]: https://github.com/manifestival/manifestival/compare/v0.2.0...v0.3.0 +[0.2.0]: https://github.com/manifestival/manifestival/compare/v0.1.0...v0.2.0 +[0.1.0]: https://github.com/manifestival/manifestival/compare/v0.0.0...v0.1.0 +[0.0.0]: https://github.com/manifestival/manifestival/releases/tag/v0.0.0 diff --git a/vendor/github.com/manifestival/manifestival/LICENSE b/vendor/github.com/manifestival/manifestival/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/vendor/github.com/manifestival/manifestival/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/manifestival/manifestival/README.md b/vendor/github.com/manifestival/manifestival/README.md new file mode 100644 index 0000000000..d1151791c9 --- /dev/null +++ b/vendor/github.com/manifestival/manifestival/README.md @@ -0,0 +1,318 @@ +# Manifestival + +[![Build Status](https://github.com/manifestival/manifestival/workflows/Build%20and%20Test/badge.svg)](https://github.com/manifestival/manifestival/actions) +[![PkgGoDev](https://pkg.go.dev/badge/github.com/manifestival/manifestival)](https://pkg.go.dev/github.com/manifestival/manifestival) + +Manifestival is a library for manipulating a set of unstructured +Kubernetes resources. Essentially, it enables you to toss a "bag of +YAML" at a k8s cluster. + +It's sort of like embedding a simplified `kubectl` in your Go +application. You can load a manifest of resources from a variety of +sources, optionally transform/filter those resources, and then +apply/delete them to/from your k8s cluster. + +See [CHANGELOG.md](CHANGELOG.md) + +* [Creating Manifests](#creating-manifests) + * [Sources](#sources) + * [Append](#append) + * [Filter](#filter) + * [Transform](#transform) +* [Applying Manifests](#applying-manifests) + * [Client](#client) + * [fake.Client](#fakeclient) + * [Logging](#logging) + * [Apply](#apply) + * [Delete](#delete) + * [DryRun](#dryrun) + + +## Creating Manifests + +Manifests may be constructed from a variety of sources. Once created, +they are immutable, but new instances may be created from them using +their [Append], [Filter] and [Transform] functions. + +The typical way to create a manifest is by calling `NewManifest` + +```go +manifest, err := NewManifest("/path/to/file.yaml") +``` + +But `NewManifest` is just a convenience function that calls +`ManifestFrom` with a `Path`, an implementation of `Source`. + +### Sources + +A manifest is created by passing an implementation of the [Source] +interface to the `ManifestFrom` function. Here are the built-in types +that implement `Source`: + +* `Path` +* `Recursive` +* `Slice` +* `Reader` + +The `Path` source is the most versatile. It's a string representing +the location of some YAML content in many possible forms: a file, a +directory of files, a URL, or a comma-delimited list of any of those +things, all of which will be combined into a single manifest. + +```go +// Single file +m, err := ManifestFrom(Path("/path/to/file.yaml")) + +// All files in a directory +m, err := ManifestFrom(Path("/path/to/dir")) + +// A remote URL +m, err := ManifestFrom(Path("http://site.com/manifest.yaml")) + +// All of the above +m, err := ManifestFrom(Path("/path/to/file.yaml,/path/to/dir,http://site.com/manifest.yaml")) +``` + +`Recursive` works exactly like `Path` except that directories are +searched recursively. + +The `Slice` source enables the creation of a manifest from an existing +slice of `[]unstructured.Unstructured`. This is helpful for testing +and, combined with the [Resources] accessor, facilitates more +sophisticated combinations of manifests, e.g. a crude form of +[Append](#append): + +```go +m3, _ := ManifestFrom(Slice(append(m1.Resources(), m2.Resources()...))) +``` + +And `Reader` is a function that takes an `io.Reader` and returns a +`Source` from which valid YAML is expected. + +### Append + +The `Append` function enables the creation of new manifests from the +concatenation of others. The resulting manifest retains the options, +e.g. client and logger, of the receiver. For example, + +```go +core, _ := NewManifest(path, UseLogger(logger), UseClient(client)) +istio, _ := NewManifest(pathToIstio) +kafka, _ := NewManifest(pathToKafka) + +manifest := core.Append(istio, kafka) +``` + +### Filter + +[Filter] returns a new Manifest containing only the resources for +which _all_ passed predicates return true. A [Predicate] is a function +that takes an `Unstructured` resource and returns a bool indicating +whether the resource should be included in the filtered results. + +There are a few built-in predicates and some helper functions for +creating your own: + +* `All` returns a `Predicate` that returns true unless any of its + arguments returns false +* `Everything` is equivalent to `All()` +* `Any` returns a `Predicate` that returns false unless any of its + arguments returns true +* `Nothing` is equivalent to `Any()` +* `Not` negates its argument, returning false if its argument returns + true +* `ByName`, `ByKind`, `ByLabel`, `ByAnnotation`, and `ByGVK` filter + resources by their respective attributes. +* `CRDs` and its complement `NoCRDs` are handy filters for + `CustomResourceDefinitions` +* `In` can be used to find the "intersection" of two manifests + +```go +clusterRBAC := Any(ByKind("ClusterRole"), ByKind("ClusterRoleBinding")) +namespaceRBAC := Any(ByKind("Role"), ByKind("RoleBinding")) +rbac := Any(clusterRBAC, namespaceRBAC) + +theRBAC := manifest.Filter(rbac) +theRest := manifest.Filter(Not(rbac)) + +// Find all resources named "controller" w/label 'foo=bar' that aren't CRD's +m := manifest.Filter(ByLabel("foo", "bar"), ByName("controller"), NoCRDs) +``` + +The `Predicate` receives a deep copy of each resource, so no +modifications made to any resource will be reflected in the returned +`Manifest`, which is immutable. The only way to alter resources in a +`Manifest` is with its `Transform` method. + + +### Transform + +[Transform] will apply some function to every resource in your +manifest, and return a new Manifest with the results. It's common for +a [Transformer] function to include a guard that simply returns if the +unstructured resource isn't of interest. + +There are a few useful transformers provided, including +`InjectNamespace` and `InjectOwner`. An example should help to +clarify: + +```go +func updateDeployment(resource *unstructured.Unstructured) error { + if resource.GetKind() != "Deployment" { + return nil + } + // Either manipulate the Unstructured resource directly or... + // convert it to a structured type... + var deployment = &appsv1.Deployment{} + if err := scheme.Scheme.Convert(resource, deployment, nil); err != nil { + return err + } + + // Now update the deployment! + + // If you converted it, convert it back, otherwise return nil + return scheme.Scheme.Convert(deployment, resource, nil) +} + +m, err := manifest.Transform(updateDeployment, InjectOwner(parent), InjectNamespace("foo")) +``` + + +## Applying Manifests + +Persisting manifests is accomplished via the [Apply] and [Delete] +functions of the [Manifestival] interface, and though [DryRun] doesn't +change anything, it does query the API Server. Therefore all of these +functions require a [Client]. + +### Client + +Manifests require a [Client] implementation to interact with a k8s API +server. There are currently two alternatives: + +- +- + +To apply your manifest, you'll need to provide a client when you +create it with the `UseClient` functional option, like so: + +```go +manifest, err := NewManifest("/path/to/file.yaml", UseClient(client)) +if err != nil { + panic("Failed to load manifest") +} +``` + +It's the `Client` that enables you to persist the resources in your +manifest using `Apply`, remove them using `Delete`, compute +differences using `DryRun`, and occasionally it's even helpful to +invoke the manifest's `Client` directly... + +```go +manifest.Apply() +manifest.Filter(NoCRDs).Delete() + +u := manifest.Resources()[0] +u.SetName("foo") +manifest.Client.Create(&u) +``` + +#### fake.Client + +The [fake] package includes a fake `Client` with stubs you can easily +override in your unit tests. For example, + +```go +func verifySomething(t *testing.T, expected *unstructured.Unstructured) { + client := fake.Client{ + fake.Stubs{ + Create: func(u *unstructured.Unstructured) error { + if !reflect.DeepEqual(u, expected) { + t.Error("You did it wrong!") + } + return nil + }, + }, + } + manifest, _ := NewManifest("testdata/some.yaml", UseClient(client)) + callSomethingThatUltimatelyAppliesThis(manifest) +} +``` + +There is also a convenient `New` function that returns a +fully-functioning fake Client by overriding the stubs to persist the +resources in a map. Here's an example using it to test the `DryRun` +function: + +```go +client := fake.New() +current, _ := NewManifest("testdata/current.yaml", UseClient(client)) +current.Apply() +modified, _ := NewManifest("testdata/modified.yaml", UseClient(client)) +diffs, err := modified.DryRun() +``` + +### Logging + +By default, Manifestival logs nothing, but it will happily log its +actions if you pass it a [logr.Logger] via its `UseLogger` functional +option, like so: + +```go +m, _ := NewManifest(path, UseLogger(log.WithName("manifestival")), UseClient(c)) +``` + +### Apply + +[Apply] will persist every resource in the manifest to the cluster. It +will invoke either `Create` or `Update` depending on whether the +resource already exists. And if it does exist, the same 3-way +[strategic merge patch] used by `kubectl apply` will be applied. And +the same annotation used by `kubectl` to record the resource's +previous configuration will be updated, too. + +The following functional options are supported, all of which map to +either the k8s `metav1.CreateOptions` and `metav1.UpdateOptions` +fields or `kubectl apply` flags: + +* `Overwrite` [true] resolve any conflicts in favor of the manifest +* `FieldManager` the name of the actor applying changes +* `DryRunAll` if present, changes won't persist + +### Delete + +[Delete] attempts to delete all the manifest's resources in reverse +order. Depending on the resources' owner references, race conditions +with the k8s garbage collector may occur, and by default `NotFound` +errors are silently ignored. + +The following functional options are supported, all except +`IgnoreNotFound` mirror the k8s `metav1.DeleteOptions`: + +* `IgnoreNotFound` [true] silently ignore any `NotFound` errors +* `GracePeriodSeconds` the number of seconds before the object should be deleted +* `Preconditions` must be fulfilled before a deletion is carried out +* `PropagationPolicy` whether and how garbage collection will be performed + +### DryRun + +[DryRun] returns a list of JSON merge patches that show the effects of +applying the manifest without modifying the live system. Each item in +the returned list is valid content for the `kubectl patch` command. + + +[Resources]: https://godoc.org/github.com/manifestival/manifestival#Manifest.Resources +[Source]: https://godoc.org/github.com/manifestival/manifestival#Source +[Manifestival]: https://godoc.org/github.com/manifestival/manifestival#Manifestival +[Append]: https://godoc.org/github.com/manifestival/manifestival#Manifest.Append +[Filter]: https://godoc.org/github.com/manifestival/manifestival#Manifest.Filter +[Transform]: https://godoc.org/github.com/manifestival/manifestival#Manifest.Transform +[Apply]: https://godoc.org/github.com/manifestival/manifestival#Manifest.Apply +[Delete]: https://godoc.org/github.com/manifestival/manifestival#Manifest.Delete +[DryRun]: https://godoc.org/github.com/manifestival/manifestival#Manifest.DryRun +[Predicate]: https://godoc.org/github.com/manifestival/manifestival#Predicate +[Client]: https://godoc.org/github.com/manifestival/manifestival#Client +[Transformer]: https://godoc.org/github.com/manifestival/manifestival#Transformer +[logr.Logger]: https://github.com/go-logr/logr +[fake]: https://godoc.org/github.com/manifestival/manifestival/fake +[strategic merge patch]: https://kubernetes.io/docs/tasks/manage-kubernetes-objects/declarative-config/#merge-patch-calculation diff --git a/vendor/github.com/manifestival/manifestival/client.go b/vendor/github.com/manifestival/manifestival/client.go new file mode 100644 index 0000000000..a8d108cede --- /dev/null +++ b/vendor/github.com/manifestival/manifestival/client.go @@ -0,0 +1,115 @@ +package manifestival + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +const ( + defaultManager = FieldManager("manifestival") +) + +// Client includes the operations required by the Manifestival interface +type Client interface { + Create(obj *unstructured.Unstructured, options ...ApplyOption) error + Update(obj *unstructured.Unstructured, options ...ApplyOption) error + Delete(obj *unstructured.Unstructured, options ...DeleteOption) error + Get(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) +} + +func ApplyWith(options []ApplyOption) *ApplyOptions { + result := &ApplyOptions{ + ForCreate: &metav1.CreateOptions{}, + ForUpdate: &metav1.UpdateOptions{}, + Overwrite: true, + } + defaultManager.ApplyWith(result) + for _, f := range options { + f.ApplyWith(result) + } + return result +} + +func DeleteWith(options []DeleteOption) *DeleteOptions { + result := &DeleteOptions{ + ForDelete: &metav1.DeleteOptions{}, + IgnoreNotFound: true, + } + for _, f := range options { + f.DeleteWith(result) + } + return result +} + +// Functional options pattern +type ApplyOption interface { + ApplyWith(*ApplyOptions) +} +type DeleteOption interface { + DeleteWith(*DeleteOptions) +} + +type ApplyOptions struct { + ForCreate *metav1.CreateOptions + ForUpdate *metav1.UpdateOptions + Overwrite bool +} +type DeleteOptions struct { + ForDelete *metav1.DeleteOptions + IgnoreNotFound bool // default to true in DeleteWith() +} + +// Indicates that changes should not be persisted +var DryRunAll = dryRunAll{} + +// FieldManager is the name of the actor applying changes +type FieldManager string + +// The duration in seconds before the object should be deleted +type GracePeriodSeconds int64 + +// Must be fulfilled before a deletion is carried out +type Preconditions metav1.Preconditions + +// Whether and how garbage collection will be performed. +type PropagationPolicy metav1.DeletionPropagation + +// Whether to error when deleting a non-existent resource [true] +type IgnoreNotFound bool + +// Resolve conflicts by using values from the manifest values +type Overwrite bool + +type dryRunAll struct{} // for both apply and delete + +func (dryRunAll) ApplyWith(opts *ApplyOptions) { + opts.ForCreate.DryRun = []string{metav1.DryRunAll} + opts.ForUpdate.DryRun = []string{metav1.DryRunAll} +} +func (i Overwrite) ApplyWith(opts *ApplyOptions) { + opts.Overwrite = bool(i) +} +func (f FieldManager) ApplyWith(opts *ApplyOptions) { + fm := string(f) + opts.ForCreate.FieldManager = fm + opts.ForUpdate.FieldManager = fm +} + +func (dryRunAll) DeleteWith(opts *DeleteOptions) { + opts.ForDelete.DryRun = []string{metav1.DryRunAll} +} +func (g GracePeriodSeconds) DeleteWith(opts *DeleteOptions) { + s := int64(g) + opts.ForDelete.GracePeriodSeconds = &s +} +func (p Preconditions) DeleteWith(opts *DeleteOptions) { + preconds := metav1.Preconditions(p) + opts.ForDelete.Preconditions = &preconds +} +func (p PropagationPolicy) DeleteWith(opts *DeleteOptions) { + policy := metav1.DeletionPropagation(p) + opts.ForDelete.PropagationPolicy = &policy +} +func (i IgnoreNotFound) DeleteWith(opts *DeleteOptions) { + opts.IgnoreNotFound = bool(i) +} diff --git a/vendor/github.com/manifestival/manifestival/dry.go b/vendor/github.com/manifestival/manifestival/dry.go new file mode 100644 index 0000000000..edeb4a6d61 --- /dev/null +++ b/vendor/github.com/manifestival/manifestival/dry.go @@ -0,0 +1,99 @@ +package manifestival + +import ( + "encoding/json" + + jsonpatch "github.com/evanphx/json-patch/v5" + "github.com/manifestival/manifestival/internal/patch" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/client-go/kubernetes/scheme" +) + +type MergePatch map[string]interface{} + +// DryRun returns a list of merge patches, either strategic or +// RFC-7386 for unregistered types, that show the effects of applying +// the manifest. +func (m Manifest) DryRun() ([]MergePatch, error) { + diffs, err := m.diff() + if err != nil { + return nil, err + } + result := make([]MergePatch, len(diffs)) + for i, bytes := range diffs { + if err := json.Unmarshal(bytes, &result[i]); err != nil { + return nil, err + } + } + return result, nil +} + +// diff loads the resources in the manifest and computes their difference +func (m Manifest) diff() ([][]byte, error) { + result := make([][]byte, 0, len(m.resources)) + for _, spec := range m.resources { + current, err := m.Client.Get(&spec) + if err != nil { + if errors.IsNotFound(err) { + // this resource will be created when applied + jmp, _ := spec.MarshalJSON() + result = append(result, jmp) + continue + } + return nil, err + } + // ignore manifestival metadata by forcing it to match + if anns := current.GetAnnotations(); anns != nil { + if v, ok := anns["manifestival"]; ok { + annotate(&spec, "manifestival", v) + } + } + // create diff + diff, err := patch.New(current, &spec) + if err != nil { + return nil, err + } + if diff == nil { + // ignore things that won't change + continue + } + // apply diff + modified := current.DeepCopy() + if err := diff.Merge(modified); err != nil { + return nil, err + } + // Remove these fields so they'll be included in the patch + current.SetAPIVersion("") + current.SetKind("") + current.SetName("") + jmp, err := mergePatch(current, modified) + if err != nil { + return nil, err + } + result = append(result, jmp) + } + return result, nil +} + +// mergePatch returns a 2-way merge patch +func mergePatch(orig, mod *unstructured.Unstructured) (_ []byte, err error) { + var original, modified []byte + if original, err = orig.MarshalJSON(); err != nil { + return + } + if modified, err = mod.MarshalJSON(); err != nil { + return + } + obj, err := scheme.Scheme.New(mod.GroupVersionKind()) + switch { + case runtime.IsNotRegisteredError(err): + return jsonpatch.CreateMergePatch(original, modified) + case err != nil: + return nil, err + default: + return strategicpatch.CreateTwoWayMergePatch(original, modified, obj) + } +} diff --git a/vendor/github.com/manifestival/manifestival/fake/client.go b/vendor/github.com/manifestival/manifestival/fake/client.go new file mode 100644 index 0000000000..60e9c31bea --- /dev/null +++ b/vendor/github.com/manifestival/manifestival/fake/client.go @@ -0,0 +1,102 @@ +package fake + +import ( + "fmt" + + mf "github.com/manifestival/manifestival" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/kubernetes/scheme" +) + +var _ mf.Client = &Client{} + +// A convenient way to stub out a Client for test fixtures. Default +// behavior does nothing and returns a nil error. +type Client struct { + Stubs +} + +// Override any of the Client functions +type Stubs struct { + Create mutator + Update mutator + Delete mutator + Get accessor +} + +type mutator func(obj *unstructured.Unstructured) error +type accessor func(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) + +// New returns a fully-functioning Client, "persisting" resources in a +// map, optionally initialized with some API objects +func New(objs ...runtime.Object) Client { + store := map[string]*unstructured.Unstructured{} + key := func(u *unstructured.Unstructured) string { + return fmt.Sprintf("%s, %s/%s", u.GroupVersionKind(), u.GetNamespace(), u.GetName()) + } + for _, obj := range objs { + u := &unstructured.Unstructured{} + if err := scheme.Scheme.Convert(obj, u, nil); err != nil { + panic(err) + } + store[key(u)] = u + } + apply := func(u *unstructured.Unstructured) error { + store[key(u)] = u + return nil + } + return Client{ + Stubs{ + Create: apply, + Update: apply, + Delete: func(u *unstructured.Unstructured) error { + delete(store, key(u)) + return nil + }, + Get: func(u *unstructured.Unstructured) (*unstructured.Unstructured, error) { + v, found := store[key(u)] + if !found { + gvk := u.GroupVersionKind() + gr := schema.GroupResource{Group: gvk.Group, Resource: gvk.Kind} + return nil, errors.NewNotFound(gr, u.GetName()) + } + return v, nil + }, + }, + } +} + +// Manifestival.Client.Create +func (c Client) Create(obj *unstructured.Unstructured, options ...mf.ApplyOption) error { + if c.Stubs.Create != nil { + return c.Stubs.Create(obj) + } + return nil +} + +// Manifestival.Client.Update +func (c Client) Update(obj *unstructured.Unstructured, options ...mf.ApplyOption) error { + if c.Stubs.Update != nil { + return c.Stubs.Update(obj) + } + return nil +} + +// Manifestival.Client.Delete +func (c Client) Delete(obj *unstructured.Unstructured, options ...mf.DeleteOption) error { + if c.Stubs.Delete != nil { + return c.Stubs.Delete(obj) + } + return nil +} + +// Manifestival.Client.Get +func (c Client) Get(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) { + if c.Stubs.Get != nil { + return c.Stubs.Get(obj) + } + return nil, nil +} diff --git a/vendor/github.com/manifestival/manifestival/filter.go b/vendor/github.com/manifestival/manifestival/filter.go new file mode 100644 index 0000000000..f8ac76d8f7 --- /dev/null +++ b/vendor/github.com/manifestival/manifestival/filter.go @@ -0,0 +1,145 @@ +package manifestival + +import ( + "fmt" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/sets" +) + +// Predicate returns true if u should be included in result +type Predicate func(u *unstructured.Unstructured) bool + +var ( + Everything = All() + Nothing = Any() +) + +// Filter returns a Manifest containing only those resources for which +// *no* Predicate returns false. Any changes callers make to the +// resources passed to their Predicate[s] will only be reflected in +// the returned Manifest. +func (m Manifest) Filter(preds ...Predicate) Manifest { + result := m + result.resources = []unstructured.Unstructured{} + pred := All(preds...) + for _, spec := range m.resources { + if !pred(spec.DeepCopy()) { + continue + } + result.resources = append(result.resources, spec) + } + return result +} + +// All returns a predicate that returns true unless any of its passed +// predicates return false. +func All(preds ...Predicate) Predicate { + return func(u *unstructured.Unstructured) bool { + for _, p := range preds { + if !p(u) { + return false + } + } + return true + } +} + +// Any returns a predicate that returns false unless any of its passed +// predicates return true. +func Any(preds ...Predicate) Predicate { + return func(u *unstructured.Unstructured) bool { + for _, p := range preds { + if p(u) { + return true + } + } + return false + } +} + +// Not returns the complement of a given predicate. +func Not(pred Predicate) Predicate { + return func(u *unstructured.Unstructured) bool { + return !pred(u) + } +} + +// CRDs returns only CustomResourceDefinitions +var CRDs = ByKind("CustomResourceDefinition") + +// NoCRDs returns no CustomResourceDefinitions +var NoCRDs = Not(CRDs) + +// ByName returns resources with a specifc name +func ByName(name string) Predicate { + return func(u *unstructured.Unstructured) bool { + return u.GetName() == name + } +} + +// ByKind returns resources matching a particular kind +func ByKind(kind string) Predicate { + return func(u *unstructured.Unstructured) bool { + return u.GetKind() == kind + } +} + +// ByAnnotation returns resources that contain a particular annotation +// and value. A value of "" denotes *ANY* value +func ByAnnotation(annotation, value string) Predicate { + return func(u *unstructured.Unstructured) bool { + v, ok := u.GetAnnotations()[annotation] + if value == "" { + return ok + } + return v == value + } +} + +// ByLabel returns resources that contain a particular label and +// value. A value of "" denotes *ANY* value +func ByLabel(label, value string) Predicate { + return func(u *unstructured.Unstructured) bool { + v, ok := u.GetLabels()[label] + if value == "" { + return ok + } + return v == value + } +} + +// ByLabels returns true when the resource contains any of the labels. +func ByLabels(labels map[string]string) Predicate { + return func(u *unstructured.Unstructured) bool { + for key, value := range labels { + if v := u.GetLabels()[key]; v == value { + return true + } + } + return false + } +} + +// ByGVK returns resources of a particular GroupVersionKind +func ByGVK(gvk schema.GroupVersionKind) Predicate { + return func(u *unstructured.Unstructured) bool { + return u.GroupVersionKind() == gvk + } +} + +// In(m) returns a Predicate that tests for membership in m, using +// "gk|ns/name" as a unique identifier +func In(manifest Manifest) Predicate { + key := func(u *unstructured.Unstructured) string { + return fmt.Sprintf("%s|%s/%s", u.GroupVersionKind().GroupKind(), u.GetNamespace(), u.GetName()) + } + index := sets.NewString() + for _, u := range manifest.resources { + index.Insert(key(&u)) + } + return func(u *unstructured.Unstructured) bool { + return index.Has(key(u)) + } +} diff --git a/vendor/github.com/manifestival/manifestival/internal/overlay/overlay.go b/vendor/github.com/manifestival/manifestival/internal/overlay/overlay.go new file mode 100644 index 0000000000..78b976bb5b --- /dev/null +++ b/vendor/github.com/manifestival/manifestival/internal/overlay/overlay.go @@ -0,0 +1,36 @@ +package overlay + +// Overlays the values of src onto tgt +func Copy(src, tgt map[string]interface{}) { + for k, v := range src { + switch y := tgt[k].(type) { + case map[string]interface{}: + if x, ok := v.(map[string]interface{}); ok { + Copy(x, y) + } else { + tgt[k] = v + } + case []interface{}: + if x, ok := v.([]interface{}); ok { + if len(y) < len(x) { + tgt[k] = v + continue + } + for i := range x { + xi, xok := x[i].(map[string]interface{}) + yi, yok := y[i].(map[string]interface{}) + if xok && yok { + Copy(xi, yi) + } else { + y[i] = x[i] + } + } + tgt[k] = y[:len(x)] + } else { + tgt[k] = v + } + default: + tgt[k] = v + } + } +} diff --git a/vendor/github.com/manifestival/manifestival/internal/patch/patch.go b/vendor/github.com/manifestival/manifestival/internal/patch/patch.go new file mode 100644 index 0000000000..3c76e7b3d5 --- /dev/null +++ b/vendor/github.com/manifestival/manifestival/internal/patch/patch.go @@ -0,0 +1,90 @@ +package patch + +import ( + "bytes" + + jsonpatch "github.com/evanphx/json-patch/v5" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/jsonmergepatch" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/client-go/kubernetes/scheme" +) + +type Patch struct { + patch []byte + schema strategicpatch.LookupPatchMeta +} + +// Attempts to create a 3-way strategic JSON merge patch. Falls back +// to RFC-7386 if object's type isn't registered +func New(curr, mod *unstructured.Unstructured) (_ *Patch, err error) { + var original, modified, current []byte + original = getLastAppliedConfig(curr) + if modified, err = mod.MarshalJSON(); err != nil { + return + } + if current, err = curr.MarshalJSON(); err != nil { + return + } + obj, err := scheme.Scheme.New(mod.GroupVersionKind()) + switch { + case runtime.IsNotRegisteredError(err): + return createJsonMergePatch(original, modified, current) + case err != nil: + return + default: + return createStrategicMergePatch(original, modified, current, obj) + } +} + +// Apply the patch to the resource +func (p *Patch) Merge(obj *unstructured.Unstructured) (err error) { + var current, result []byte + if current, err = obj.MarshalJSON(); err != nil { + return + } + if p.schema == nil { + result, err = jsonpatch.MergePatch(current, p.patch) + } else { + result, err = strategicpatch.StrategicMergePatchUsingLookupPatchMeta(current, p.patch, p.schema) + } + if err != nil { + return + } + return obj.UnmarshalJSON(result) +} + +func (p *Patch) String() string { + return string(p.patch) +} + +func createJsonMergePatch(original, modified, current []byte) (*Patch, error) { + patch, err := jsonmergepatch.CreateThreeWayJSONMergePatch(original, modified, current) + return create(patch, nil), err +} + +func createStrategicMergePatch(original, modified, current []byte, obj runtime.Object) (*Patch, error) { + schema, err := strategicpatch.NewPatchMetaFromStruct(obj) + if err != nil { + return nil, err + } + patch, err := strategicpatch.CreateThreeWayMergePatch(original, modified, current, schema, true) + return create(patch, schema), err +} + +func create(patch []byte, schema strategicpatch.LookupPatchMeta) *Patch { + if bytes.Equal(patch, []byte("{}")) { + return nil + } + return &Patch{patch, schema} +} + +func getLastAppliedConfig(obj *unstructured.Unstructured) []byte { + annotations := obj.GetAnnotations() + if annotations == nil { + return nil + } + return []byte(annotations[v1.LastAppliedConfigAnnotation]) +} diff --git a/vendor/github.com/manifestival/manifestival/internal/sources/path.go b/vendor/github.com/manifestival/manifestival/internal/sources/path.go new file mode 100644 index 0000000000..37dbb6f95b --- /dev/null +++ b/vendor/github.com/manifestival/manifestival/internal/sources/path.go @@ -0,0 +1,120 @@ +package sources + +import ( + "io/ioutil" + "net/http" + "net/url" + "os" + "path" + "strings" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +// Parse parses YAML files into Unstructured objects. +// +// It supports 5 cases today: +// 1. pathname = path to a file --> parses that file. +// 2. pathname = path to a directory, recursive = false --> parses all files in +// that directory. +// 3. pathname = path to a directory, recursive = true --> parses all files in +// that directory and it's descendants +// 4. pathname = url --> fetches the contents of that URL and parses them as YAML. +// 5. pathname = combination of all previous cases, the string can contain +// multiple records (file, directory or url) separated by comma +func Parse(pathname string, recursive bool) ([]unstructured.Unstructured, error) { + + pathnames := strings.Split(pathname, ",") + aggregated := []unstructured.Unstructured{} + for _, pth := range pathnames { + els, err := read(pth, recursive) + if err != nil { + return nil, err + } + + aggregated = append(aggregated, els...) + } + return aggregated, nil +} + +// read cotains a logic to distinguish the type of record in pathname +// (file, directory or url) and calls the appropriate function +func read(pathname string, recursive bool) ([]unstructured.Unstructured, error) { + if isURL(pathname) { + return readURL(pathname) + } + + info, err := os.Stat(pathname) + if err != nil { + return nil, err + } + + if info.IsDir() { + return readDir(pathname, recursive) + } + return readFile(pathname) +} + +// readFile parses a single file. +func readFile(pathname string) ([]unstructured.Unstructured, error) { + file, err := os.Open(pathname) + if err != nil { + return nil, err + } + defer file.Close() + + return Decode(file) +} + +// readDir parses all files in a single directory and it's descendant directories +// if the recursive flag is set to true. +func readDir(pathname string, recursive bool) ([]unstructured.Unstructured, error) { + list, err := ioutil.ReadDir(pathname) + if err != nil { + return nil, err + } + + aggregated := []unstructured.Unstructured{} + for _, f := range list { + name := path.Join(pathname, f.Name()) + pathDirOrFile, err := os.Stat(name) + var els []unstructured.Unstructured + + if os.IsNotExist(err) || os.IsPermission(err) { + return aggregated, err + } + + switch { + case pathDirOrFile.IsDir() && recursive: + els, err = readDir(name, recursive) + case !pathDirOrFile.IsDir(): + els, err = readFile(name) + } + + if err != nil { + return nil, err + } + aggregated = append(aggregated, els...) + } + return aggregated, nil +} + +// readURL fetches a URL and parses its contents as YAML. +func readURL(url string) ([]unstructured.Unstructured, error) { + resp, err := http.Get(url) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + return Decode(resp.Body) +} + +// isURL checks whether or not the given path parses as a URL. +func isURL(pathname string) bool { + if _, err := os.Lstat(pathname); err == nil { + return false + } + url, err := url.ParseRequestURI(pathname) + return err == nil && url.Scheme != "" +} diff --git a/vendor/github.com/manifestival/manifestival/internal/sources/yaml.go b/vendor/github.com/manifestival/manifestival/internal/sources/yaml.go new file mode 100644 index 0000000000..c4f2b98c56 --- /dev/null +++ b/vendor/github.com/manifestival/manifestival/internal/sources/yaml.go @@ -0,0 +1,30 @@ +package sources + +import ( + "io" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/util/yaml" +) + +// decode consumes the given reader and parses its contents as YAML. +func Decode(reader io.Reader) ([]unstructured.Unstructured, error) { + decoder := yaml.NewYAMLToJSONDecoder(reader) + objs := []unstructured.Unstructured{} + var err error + for { + out := unstructured.Unstructured{} + err = decoder.Decode(&out) + if err != nil { + break + } + if len(out.Object) == 0 { + continue + } + objs = append(objs, out) + } + if err != io.EOF { + return nil, err + } + return objs, nil +} diff --git a/vendor/github.com/manifestival/manifestival/manifestival.go b/vendor/github.com/manifestival/manifestival/manifestival.go new file mode 100644 index 0000000000..b8d07b8bc2 --- /dev/null +++ b/vendor/github.com/manifestival/manifestival/manifestival.go @@ -0,0 +1,244 @@ +package manifestival + +import ( + "fmt" + + "github.com/go-logr/logr" + "github.com/manifestival/manifestival/internal/overlay" + "github.com/manifestival/manifestival/internal/patch" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +// Manifestival defines the operations allowed on a set of Kubernetes +// resources (typically, a set of YAML files, aka a manifest) +type Manifestival interface { + // Either updates or creates all resources in the manifest + Apply(opts ...ApplyOption) error + // Deletes all resources in the manifest + Delete(opts ...DeleteOption) error + // Transforms the resources within a Manifest + Transform(fns ...Transformer) (Manifest, error) + // Filters resources in a Manifest; Predicates are AND'd + Filter(fns ...Predicate) Manifest + // Append the resources from other Manifests to create a new one + Append(mfs ...Manifest) Manifest + // Show how applying the manifest would change the cluster + DryRun() ([]MergePatch, error) +} + +// Manifest tracks a set of concrete resources which should be managed as a +// group using a Kubernetes client +type Manifest struct { + resources []unstructured.Unstructured + Client Client + log logr.Logger +} + +var _ Manifestival = &Manifest{} + +// Option follows the "functional object" idiom +type Option func(*Manifest) + +// UseLogger will cause manifestival to log its actions +func UseLogger(log logr.Logger) Option { + return func(m *Manifest) { + m.log = log + } +} + +// UseClient enables interaction with the k8s API server +func UseClient(client Client) Option { + return func(m *Manifest) { + m.Client = client + } +} + +// NewManifest creates a Manifest from a comma-separated set of YAML +// files, directories, or URLs. It's equivalent to +// `ManifestFrom(Path(pathname))` +func NewManifest(pathname string, opts ...Option) (Manifest, error) { + return ManifestFrom(Path(pathname), opts...) +} + +// ManifestFrom creates a Manifest from any Source implementation +func ManifestFrom(src Source, opts ...Option) (m Manifest, err error) { + m = Manifest{log: logr.Discard()} + for _, opt := range opts { + opt(&m) + } + m.log.Info("Parsing manifest") + m.resources, err = src.Parse() + return +} + +// Append creates a new Manifest by appending the resources from other +// Manifests onto this one. No equality checking is done, so for any +// resources sharing the same GVK+name, the last one will "win". +func (m Manifest) Append(mfs ...Manifest) Manifest { + result := m + result.resources = m.Resources() // deep copies + for _, mf := range mfs { + result.resources = append(result.resources, mf.Resources()...) + } + return result +} + +// Resources returns a deep copy of the Manifest resources +func (m Manifest) Resources() []unstructured.Unstructured { + result := make([]unstructured.Unstructured, len(m.resources)) + for i, v := range m.resources { + result[i] = *v.DeepCopy() + } + return result +} + +// Apply updates or creates all resources in the manifest. +func (m Manifest) Apply(opts ...ApplyOption) error { + for _, spec := range m.resources { + if err := m.apply(&spec, opts...); err != nil { + return err + } + } + return nil +} + +// Delete removes all resources in the Manifest +func (m Manifest) Delete(opts ...DeleteOption) error { + a := make([]unstructured.Unstructured, len(m.resources)) + copy(a, m.resources) // shallow copy is fine + // we want to delete in reverse order + for left, right := 0, len(a)-1; left < right; left, right = left+1, right-1 { + a[left], a[right] = a[right], a[left] + } + for _, spec := range a { + if err := m.delete(&spec, opts...); err != nil { + return err + } + } + return nil +} + +// apply updates or creates a particular resource +func (m Manifest) apply(spec *unstructured.Unstructured, opts ...ApplyOption) error { + current, err := m.get(spec) + if err != nil { + return err + } + if current == nil { + m.logResource("Creating", spec) + current = spec.DeepCopy() + annotate(current, "manifestival", resourceCreated) + annotate(current, v1.LastAppliedConfigAnnotation, lastApplied(current)) + return m.Client.Create(current, opts...) + } else { + diff, err := patch.New(current, spec) + if err != nil { + return err + } + if diff == nil { + return nil + } + + isResourceCreated := current.GetAnnotations()["manifestival"] == resourceCreated + m.log.Info("Merging", "diff", diff) + if err := diff.Merge(current); err != nil { + return err + } + + // Make sure the manifestival annotation is carried over. + if isResourceCreated { + annotate(current, "manifestival", resourceCreated) + } + + return m.update(current, spec, opts...) + } +} + +// update a single resource +func (m Manifest) update(live, spec *unstructured.Unstructured, opts ...ApplyOption) error { + m.logResource("Updating", live) + annotate(live, v1.LastAppliedConfigAnnotation, lastApplied(spec)) + err := m.Client.Update(live, opts...) + if errors.IsInvalid(err) && ApplyWith(opts).Overwrite { + m.log.Error(err, "Failed to update merged resource, trying overwrite") + overlay.Copy(spec.Object, live.Object) + return m.Client.Update(live, opts...) + } + return err +} + +// delete removes the specified object +func (m Manifest) delete(spec *unstructured.Unstructured, opts ...DeleteOption) error { + current, err := m.get(spec) + if err != nil { + return err + } + if current == nil { + return nil + } + if !okToDelete(current) { + return nil + } + m.logResource("Deleting", spec) + return m.Client.Delete(spec, opts...) +} + +// get collects a full resource body (or `nil`) from a partial +// resource supplied in `spec` +func (m Manifest) get(spec *unstructured.Unstructured) (*unstructured.Unstructured, error) { + if spec.GetName() == "" && spec.GetGenerateName() != "" { + // expected to be created; never fetched + return nil, nil + } + result, err := m.Client.Get(spec) + if err != nil { + result = nil + if errors.IsNotFound(err) { + err = nil + } + } + return result, err +} + +// logResource logs a consistent formatted message +func (m Manifest) logResource(msg string, spec *unstructured.Unstructured) { + name := fmt.Sprintf("%s/%s", spec.GetNamespace(), spec.GetName()) + m.log.Info(msg, "name", name, "type", spec.GroupVersionKind()) +} + +// annotate sets an annotation in the resource +func annotate(spec *unstructured.Unstructured, key string, value string) { + annotations := spec.GetAnnotations() + if annotations == nil { + annotations = make(map[string]string) + } + annotations[key] = value + spec.SetAnnotations(annotations) +} + +// lastApplied returns a JSON string denoting the resource's state +func lastApplied(obj *unstructured.Unstructured) string { + ann := obj.GetAnnotations() + if len(ann) > 0 { + delete(ann, v1.LastAppliedConfigAnnotation) + obj.SetAnnotations(ann) + } + bytes, _ := obj.MarshalJSON() + return string(bytes) +} + +// okToDelete checks for an annotation indicating that the resources +// was originally created by this library +func okToDelete(spec *unstructured.Unstructured) bool { + switch spec.GetKind() { + case "Namespace": + return spec.GetAnnotations()["manifestival"] == resourceCreated + } + return true +} + +const ( + resourceCreated = "new" +) diff --git a/vendor/github.com/manifestival/manifestival/source.go b/vendor/github.com/manifestival/manifestival/source.go new file mode 100644 index 0000000000..bd83db1e74 --- /dev/null +++ b/vendor/github.com/manifestival/manifestival/source.go @@ -0,0 +1,53 @@ +package manifestival + +import ( + "io" + + "github.com/manifestival/manifestival/internal/sources" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +// Source is the interface through which all Manifests are created. +type Source interface { + Parse() ([]unstructured.Unstructured, error) +} + +// Path is a Source represented as a comma-delimited list of files, +// directories, and URL's +type Path string + +// Recursive is identical to Path, but dirs are searched recursively +type Recursive string + +// Slice is a Source comprised of existing objects +type Slice []unstructured.Unstructured + +// Reader takes an io.Reader from which YAML content is expected +func Reader(r io.Reader) Source { + return reader{r} +} + +var _ Source = Path("") +var _ Source = Recursive("") +var _ Source = Slice([]unstructured.Unstructured{}) +var _ Source = reader{} // see Reader(io.Reader) + +func (p Path) Parse() ([]unstructured.Unstructured, error) { + return sources.Parse(string(p), false) +} + +func (r Recursive) Parse() ([]unstructured.Unstructured, error) { + return sources.Parse(string(r), true) +} + +func (s Slice) Parse() ([]unstructured.Unstructured, error) { + return []unstructured.Unstructured(s), nil +} + +func (r reader) Parse() ([]unstructured.Unstructured, error) { + return sources.Decode(r.real) +} + +type reader struct { + real io.Reader +} diff --git a/vendor/github.com/manifestival/manifestival/transform.go b/vendor/github.com/manifestival/manifestival/transform.go new file mode 100644 index 0000000000..b1f9102ccc --- /dev/null +++ b/vendor/github.com/manifestival/manifestival/transform.go @@ -0,0 +1,135 @@ +package manifestival + +import ( + "os" + "strings" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// Transformer transforms a resource from the manifest in place. +type Transformer func(u *unstructured.Unstructured) error + +// Owner is a partial Kubernetes metadata schema. +type Owner interface { + v1.Object + schema.ObjectKind +} + +// Transform applies an ordered set of Transformer functions to the +// `Resources` in this Manifest. If an error occurs, no resources are +// transformed. +func (m Manifest) Transform(fns ...Transformer) (Manifest, error) { + result := m + result.resources = m.Resources() // deep copies + for i := range result.resources { + spec := &result.resources[i] + for _, transform := range fns { + if transform != nil { + err := transform(spec) + if err != nil { + return Manifest{}, err + } + } + } + } + return result, nil +} + +// InjectNamespace creates a Transformer which adds a namespace to existing +// resources if appropriate. We assume all resources in the manifest live in +// the same namespace. +func InjectNamespace(ns string) Transformer { + namespace := resolveEnv(ns) + updateService := func(obj map[string]interface{}, fields ...string) error { + srv, found, err := unstructured.NestedFieldNoCopy(obj, fields...) + if err != nil { + return err + } + if found { + m := srv.(map[string]interface{}) + if _, ok := m["namespace"]; ok { + m["namespace"] = namespace + } + } + return nil + } + return func(u *unstructured.Unstructured) error { + if !isClusterScoped(u.GetKind()) { + u.SetNamespace(namespace) + } + switch strings.ToLower(u.GetKind()) { + case "namespace": + u.SetName(namespace) + case "clusterrolebinding", "rolebinding": + subjects, _, _ := unstructured.NestedFieldNoCopy(u.Object, "subjects") + for _, subject := range subjects.([]interface{}) { + m := subject.(map[string]interface{}) + if _, ok := m["namespace"]; ok { + m["namespace"] = namespace + } + } + case "validatingwebhookconfiguration", "mutatingwebhookconfiguration": + hooks, _, _ := unstructured.NestedFieldNoCopy(u.Object, "webhooks") + for _, hook := range hooks.([]interface{}) { + if err := updateService(hook.(map[string]interface{}), "clientConfig", "service"); err != nil { + return err + } + } + case "apiservice": + return updateService(u.Object, "spec", "service") + case "customresourcedefinition": + if u.GroupVersionKind().Version == "v1" { + return updateService(u.Object, "spec", "conversion", "webhook", "clientConfig", "service") + } + return updateService(u.Object, "spec", "conversion", "webhookClientConfig", "service") + } + return nil + } +} + +// InjectOwner creates a Transformer which adds an OwnerReference +// pointing to `owner` +func InjectOwner(owner Owner) Transformer { + return func(u *unstructured.Unstructured) error { + u.SetOwnerReferences([]v1.OwnerReference{*v1.NewControllerRef(owner, owner.GroupVersionKind())}) + return nil + } +} + +func isClusterScoped(kind string) bool { + // TODO: something more clever using !APIResource.Namespaced maybe? + switch strings.ToLower(kind) { + case "componentstatus", + "namespace", + "node", + "persistentvolume", + "mutatingwebhookconfiguration", + "validatingwebhookconfiguration", + "customresourcedefinition", + "apiservice", + "meshpolicy", + "tokenreview", + "selfsubjectaccessreview", + "selfsubjectrulesreview", + "subjectaccessreview", + "certificatesigningrequest", + "podsecuritypolicy", + "clusterrolebinding", + "clusterrole", + "priorityclass", + "storageclass", + "volumeattachment": + return true + } + return false +} + +func resolveEnv(x string) string { + if len(x) > 1 && x[:1] == "$" { + return os.Getenv(x[1:]) + } + return x +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 8cedb3bbbb..b2169480a2 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -825,6 +825,17 @@ github.com/magiconair/properties github.com/mailru/easyjson/buffer github.com/mailru/easyjson/jlexer github.com/mailru/easyjson/jwriter +# github.com/manifestival/client-go-client v0.5.0 +## explicit; go 1.15 +github.com/manifestival/client-go-client +github.com/manifestival/client-go-client/pkg/dynamic +# github.com/manifestival/manifestival v0.7.2 +## explicit; go 1.15 +github.com/manifestival/manifestival +github.com/manifestival/manifestival/fake +github.com/manifestival/manifestival/internal/overlay +github.com/manifestival/manifestival/internal/patch +github.com/manifestival/manifestival/internal/sources # github.com/mattn/go-colorable v0.1.13 ## explicit; go 1.15 github.com/mattn/go-colorable @@ -1807,6 +1818,9 @@ knative.dev/serving/pkg/reconciler/route/resources/labels knative.dev/serving/pkg/reconciler/route/resources/names knative.dev/serving/pkg/reconciler/service/resources/names knative.dev/serving/pkg/testing/v1 +# sigs.k8s.io/controller-runtime v0.7.2 +## explicit; go 1.15 +sigs.k8s.io/controller-runtime/pkg/client/apiutil # sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd ## explicit; go 1.18 sigs.k8s.io/json diff --git a/vendor/sigs.k8s.io/controller-runtime/LICENSE b/vendor/sigs.k8s.io/controller-runtime/LICENSE new file mode 100644 index 0000000000..8dada3edaf --- /dev/null +++ b/vendor/sigs.k8s.io/controller-runtime/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/apimachinery.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/apimachinery.go new file mode 100644 index 0000000000..b3464c655d --- /dev/null +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/apimachinery.go @@ -0,0 +1,151 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package apiutil contains utilities for working with raw Kubernetes +// API machinery, such as creating RESTMappers and raw REST clients, +// and extracting the GVK of an object. +package apiutil + +import ( + "fmt" + "sync" + + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/client-go/discovery" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "k8s.io/client-go/restmapper" +) + +var ( + protobufScheme = runtime.NewScheme() + protobufSchemeLock sync.RWMutex +) + +func init() { + // Currently only enabled for built-in resources which are guaranteed to implement Protocol Buffers. + // For custom resources, CRDs can not support Protocol Buffers but Aggregated API can. + // See doc: https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/#advanced-features-and-flexibility + if err := clientgoscheme.AddToScheme(protobufScheme); err != nil { + panic(err) + } +} + +// AddToProtobufScheme add the given SchemeBuilder into protobufScheme, which should +// be additional types that do support protobuf. +func AddToProtobufScheme(addToScheme func(*runtime.Scheme) error) error { + protobufSchemeLock.Lock() + defer protobufSchemeLock.Unlock() + return addToScheme(protobufScheme) +} + +// NewDiscoveryRESTMapper constructs a new RESTMapper based on discovery +// information fetched by a new client with the given config. +func NewDiscoveryRESTMapper(c *rest.Config) (meta.RESTMapper, error) { + // Get a mapper + dc, err := discovery.NewDiscoveryClientForConfig(c) + if err != nil { + return nil, err + } + gr, err := restmapper.GetAPIGroupResources(dc) + if err != nil { + return nil, err + } + return restmapper.NewDiscoveryRESTMapper(gr), nil +} + +// GVKForObject finds the GroupVersionKind associated with the given object, if there is only a single such GVK. +func GVKForObject(obj runtime.Object, scheme *runtime.Scheme) (schema.GroupVersionKind, error) { + // TODO(directxman12): do we want to generalize this to arbitrary container types? + // I think we'd need a generalized form of scheme or something. It's a + // shame there's not a reliable "GetGVK" interface that works by default + // for unpopulated static types and populated "dynamic" types + // (unstructured, partial, etc) + + // check for PartialObjectMetadata, which is analogous to unstructured, but isn't handled by ObjectKinds + _, isPartial := obj.(*metav1.PartialObjectMetadata) + _, isPartialList := obj.(*metav1.PartialObjectMetadataList) + if isPartial || isPartialList { + // we require that the GVK be populated in order to recognize the object + gvk := obj.GetObjectKind().GroupVersionKind() + if len(gvk.Kind) == 0 { + return schema.GroupVersionKind{}, runtime.NewMissingKindErr("unstructured object has no kind") + } + if len(gvk.Version) == 0 { + return schema.GroupVersionKind{}, runtime.NewMissingVersionErr("unstructured object has no version") + } + return gvk, nil + } + + gvks, isUnversioned, err := scheme.ObjectKinds(obj) + if err != nil { + return schema.GroupVersionKind{}, err + } + if isUnversioned { + return schema.GroupVersionKind{}, fmt.Errorf("cannot create group-version-kind for unversioned type %T", obj) + } + + if len(gvks) < 1 { + return schema.GroupVersionKind{}, fmt.Errorf("no group-version-kinds associated with type %T", obj) + } + if len(gvks) > 1 { + // this should only trigger for things like metav1.XYZ -- + // normal versioned types should be fine + return schema.GroupVersionKind{}, fmt.Errorf( + "multiple group-version-kinds associated with type %T, refusing to guess at one", obj) + } + return gvks[0], nil +} + +// RESTClientForGVK constructs a new rest.Interface capable of accessing the resource associated +// with the given GroupVersionKind. The REST client will be configured to use the negotiated serializer from +// baseConfig, if set, otherwise a default serializer will be set. +func RESTClientForGVK(gvk schema.GroupVersionKind, isUnstructured bool, baseConfig *rest.Config, codecs serializer.CodecFactory) (rest.Interface, error) { + cfg := createRestConfig(gvk, isUnstructured, baseConfig) + if cfg.NegotiatedSerializer == nil { + cfg.NegotiatedSerializer = serializer.WithoutConversionCodecFactory{CodecFactory: codecs} + } + return rest.RESTClientFor(cfg) +} + +//createRestConfig copies the base config and updates needed fields for a new rest config +func createRestConfig(gvk schema.GroupVersionKind, isUnstructured bool, baseConfig *rest.Config) *rest.Config { + gv := gvk.GroupVersion() + + cfg := rest.CopyConfig(baseConfig) + cfg.GroupVersion = &gv + if gvk.Group == "" { + cfg.APIPath = "/api" + } else { + cfg.APIPath = "/apis" + } + if cfg.UserAgent == "" { + cfg.UserAgent = rest.DefaultKubernetesUserAgent() + } + // TODO(FillZpp): In the long run, we want to check discovery or something to make sure that this is actually true. + if cfg.ContentType == "" && !isUnstructured { + protobufSchemeLock.RLock() + if protobufScheme.Recognizes(gvk) { + cfg.ContentType = runtime.ContentTypeProtobuf + } + protobufSchemeLock.RUnlock() + } + return cfg +} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/dynamicrestmapper.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/dynamicrestmapper.go new file mode 100644 index 0000000000..5e9a7b5f53 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/dynamicrestmapper.go @@ -0,0 +1,285 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package apiutil + +import ( + "errors" + "sync" + + "golang.org/x/time/rate" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/discovery" + "k8s.io/client-go/rest" + "k8s.io/client-go/restmapper" +) + +// dynamicRESTMapper is a RESTMapper that dynamically discovers resource +// types at runtime. +type dynamicRESTMapper struct { + mu sync.RWMutex // protects the following fields + staticMapper meta.RESTMapper + limiter *rate.Limiter + newMapper func() (meta.RESTMapper, error) + + lazy bool + // Used for lazy init. + initOnce sync.Once +} + +// DynamicRESTMapperOption is a functional option on the dynamicRESTMapper +type DynamicRESTMapperOption func(*dynamicRESTMapper) error + +// WithLimiter sets the RESTMapper's underlying limiter to lim. +func WithLimiter(lim *rate.Limiter) DynamicRESTMapperOption { + return func(drm *dynamicRESTMapper) error { + drm.limiter = lim + return nil + } +} + +// WithLazyDiscovery prevents the RESTMapper from discovering REST mappings +// until an API call is made. +var WithLazyDiscovery DynamicRESTMapperOption = func(drm *dynamicRESTMapper) error { + drm.lazy = true + return nil +} + +// WithCustomMapper supports setting a custom RESTMapper refresher instead of +// the default method, which uses a discovery client. +// +// This exists mainly for testing, but can be useful if you need tighter control +// over how discovery is performed, which discovery endpoints are queried, etc. +func WithCustomMapper(newMapper func() (meta.RESTMapper, error)) DynamicRESTMapperOption { + return func(drm *dynamicRESTMapper) error { + drm.newMapper = newMapper + return nil + } +} + +// NewDynamicRESTMapper returns a dynamic RESTMapper for cfg. The dynamic +// RESTMapper dynamically discovers resource types at runtime. opts +// configure the RESTMapper. +func NewDynamicRESTMapper(cfg *rest.Config, opts ...DynamicRESTMapperOption) (meta.RESTMapper, error) { + client, err := discovery.NewDiscoveryClientForConfig(cfg) + if err != nil { + return nil, err + } + drm := &dynamicRESTMapper{ + limiter: rate.NewLimiter(rate.Limit(defaultRefillRate), defaultLimitSize), + newMapper: func() (meta.RESTMapper, error) { + groupResources, err := restmapper.GetAPIGroupResources(client) + if err != nil { + return nil, err + } + return restmapper.NewDiscoveryRESTMapper(groupResources), nil + }, + } + for _, opt := range opts { + if err = opt(drm); err != nil { + return nil, err + } + } + if !drm.lazy { + if err := drm.setStaticMapper(); err != nil { + return nil, err + } + } + return drm, nil +} + +var ( + // defaultRefilRate is the default rate at which potential calls are + // added back to the "bucket" of allowed calls. + defaultRefillRate = 5 + // defaultLimitSize is the default starting/max number of potential calls + // per second. Once a call is used, it's added back to the bucket at a rate + // of defaultRefillRate per second. + defaultLimitSize = 5 +) + +// setStaticMapper sets drm's staticMapper by querying its client, regardless +// of reload backoff. +func (drm *dynamicRESTMapper) setStaticMapper() error { + newMapper, err := drm.newMapper() + if err != nil { + return err + } + drm.staticMapper = newMapper + return nil +} + +// init initializes drm only once if drm is lazy. +func (drm *dynamicRESTMapper) init() (err error) { + drm.initOnce.Do(func() { + if drm.lazy { + err = drm.setStaticMapper() + } + }) + return err +} + +// checkAndReload attempts to call the given callback, which is assumed to be dependent +// on the data in the restmapper. +// +// If the callback returns an error that matches the given error, it will attempt to reload +// the RESTMapper's data and re-call the callback once that's occurred. +// If the callback returns any other error, the function will return immediately regardless. +// +// It will take care of ensuring that reloads are rate-limited and that extraneous calls +// aren't made. If a reload would exceed the limiters rate, it returns the error return by +// the callback. +// It's thread-safe, and worries about thread-safety for the callback (so the callback does +// not need to attempt to lock the restmapper). +func (drm *dynamicRESTMapper) checkAndReload(needsReloadErr error, checkNeedsReload func() error) error { + // first, check the common path -- data is fresh enough + // (use an IIFE for the lock's defer) + err := func() error { + drm.mu.RLock() + defer drm.mu.RUnlock() + + return checkNeedsReload() + }() + + // NB(directxman12): `Is` and `As` have a confusing relationship -- + // `Is` is like `== or does this implement .Is`, whereas `As` says + // `can I type-assert into` + needsReload := errors.As(err, &needsReloadErr) + if !needsReload { + return err + } + + // if the data wasn't fresh, we'll need to try and update it, so grab the lock... + drm.mu.Lock() + defer drm.mu.Unlock() + + // ... and double-check that we didn't reload in the meantime + err = checkNeedsReload() + needsReload = errors.As(err, &needsReloadErr) + if !needsReload { + return err + } + + // we're still stale, so grab a rate-limit token if we can... + if !drm.limiter.Allow() { + // return error from static mapper here, we have refreshed often enough (exceeding rate of provided limiter) + // so that client's can handle this the same way as a "normal" NoResourceMatchError / NoKindMatchError + return err + } + + // ...reload... + if err := drm.setStaticMapper(); err != nil { + return err + } + + // ...and return the results of the closure regardless + return checkNeedsReload() +} + +// TODO: wrap reload errors on NoKindMatchError with go 1.13 errors. + +func (drm *dynamicRESTMapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) { + if err := drm.init(); err != nil { + return schema.GroupVersionKind{}, err + } + var gvk schema.GroupVersionKind + err := drm.checkAndReload(&meta.NoResourceMatchError{}, func() error { + var err error + gvk, err = drm.staticMapper.KindFor(resource) + return err + }) + return gvk, err +} + +func (drm *dynamicRESTMapper) KindsFor(resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error) { + if err := drm.init(); err != nil { + return nil, err + } + var gvks []schema.GroupVersionKind + err := drm.checkAndReload(&meta.NoResourceMatchError{}, func() error { + var err error + gvks, err = drm.staticMapper.KindsFor(resource) + return err + }) + return gvks, err +} + +func (drm *dynamicRESTMapper) ResourceFor(input schema.GroupVersionResource) (schema.GroupVersionResource, error) { + if err := drm.init(); err != nil { + return schema.GroupVersionResource{}, err + } + + var gvr schema.GroupVersionResource + err := drm.checkAndReload(&meta.NoResourceMatchError{}, func() error { + var err error + gvr, err = drm.staticMapper.ResourceFor(input) + return err + }) + return gvr, err +} + +func (drm *dynamicRESTMapper) ResourcesFor(input schema.GroupVersionResource) ([]schema.GroupVersionResource, error) { + if err := drm.init(); err != nil { + return nil, err + } + var gvrs []schema.GroupVersionResource + err := drm.checkAndReload(&meta.NoResourceMatchError{}, func() error { + var err error + gvrs, err = drm.staticMapper.ResourcesFor(input) + return err + }) + return gvrs, err +} + +func (drm *dynamicRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*meta.RESTMapping, error) { + if err := drm.init(); err != nil { + return nil, err + } + var mapping *meta.RESTMapping + err := drm.checkAndReload(&meta.NoKindMatchError{}, func() error { + var err error + mapping, err = drm.staticMapper.RESTMapping(gk, versions...) + return err + }) + return mapping, err +} + +func (drm *dynamicRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*meta.RESTMapping, error) { + if err := drm.init(); err != nil { + return nil, err + } + var mappings []*meta.RESTMapping + err := drm.checkAndReload(&meta.NoKindMatchError{}, func() error { + var err error + mappings, err = drm.staticMapper.RESTMappings(gk, versions...) + return err + }) + return mappings, err +} + +func (drm *dynamicRESTMapper) ResourceSingularizer(resource string) (string, error) { + if err := drm.init(); err != nil { + return "", err + } + var singular string + err := drm.checkAndReload(&meta.NoResourceMatchError{}, func() error { + var err error + singular, err = drm.staticMapper.ResourceSingularizer(resource) + return err + }) + return singular, err +}