mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-25 07:48:17 +00:00
Compare commits
43 Commits
cmd/config
...
monopole-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96ac25fff5 | ||
|
|
d2f0b1b345 | ||
|
|
c95a40933b | ||
|
|
bc7b880ab1 | ||
|
|
e004c31700 | ||
|
|
ca9aa62c26 | ||
|
|
cee7cb6589 | ||
|
|
4f905c9cff | ||
|
|
232c1c8ee9 | ||
|
|
61cf3e6ec5 | ||
|
|
1ce469f1fd | ||
|
|
a49c9de4a4 | ||
|
|
bada055cd3 | ||
|
|
d7e0b1ac31 | ||
|
|
5549035b69 | ||
|
|
025200cc12 | ||
|
|
154939803f | ||
|
|
64c30a0678 | ||
|
|
b7bef5dc44 | ||
|
|
0075d0a88c | ||
|
|
3fc359043a | ||
|
|
6b6a74af19 | ||
|
|
89fc3cbb94 | ||
|
|
437be2831f | ||
|
|
b05ab6e0e3 | ||
|
|
7097013426 | ||
|
|
29fbc564e3 | ||
|
|
42abcbd516 | ||
|
|
b7b7a5a79f | ||
|
|
ebcc49d064 | ||
|
|
807ca9c1e3 | ||
|
|
6cdcb1f436 | ||
|
|
91da8525c1 | ||
|
|
b604f03740 | ||
|
|
422ba21df0 | ||
|
|
20e13abbb4 | ||
|
|
5975761fbf | ||
|
|
baccf58ccf | ||
|
|
c7bdb3fbe4 | ||
|
|
967fe44e3f | ||
|
|
d0602c732b | ||
|
|
a4179fa87f | ||
|
|
c9bce3fc0a |
7
Makefile
7
Makefile
@@ -17,6 +17,13 @@ verify-kustomize: \
|
||||
test-examples-kustomize-against-HEAD \
|
||||
test-examples-kustomize-against-latest
|
||||
|
||||
# The following target referenced by a file in
|
||||
# https://github.com/kubernetes/test-infra/tree/master/config/jobs/kubernetes-sigs/kustomize
|
||||
.PHONY: prow-presubmit-check
|
||||
prow-presubmit-check: \
|
||||
lint-kustomize \
|
||||
test-unit-kustomize-all
|
||||
|
||||
.PHONY: verify-kustomize-e2e
|
||||
verify-kustomize-e2e: test-examples-e2e-kustomize
|
||||
|
||||
|
||||
@@ -14,11 +14,6 @@ import (
|
||||
"sigs.k8s.io/kustomize/api/internal/crawl/index"
|
||||
)
|
||||
|
||||
const (
|
||||
githubAccessTokenVar = "GITHUB_ACCESS_TOKEN"
|
||||
retryCount = 3
|
||||
)
|
||||
|
||||
// iterateArr adds each item in arr into countMap.
|
||||
func iterateArr(arr []string, countMap map[string]int) {
|
||||
for _, item := range arr {
|
||||
|
||||
7
api/internal/crawl/cmd/log-parser/kustomize-stats-cmd
Normal file
7
api/internal/crawl/cmd/log-parser/kustomize-stats-cmd
Normal file
@@ -0,0 +1,7 @@
|
||||
wget <log-file-url> -O log
|
||||
go build .
|
||||
./log-parser log >out
|
||||
cat out | grep "kind \`" | cut -d\` -f2 | tail -n 50
|
||||
cat out | grep "kind \`" | awk '{print $6}' | tail -n 50
|
||||
cat out | grep "feature \`" | grep -v "\`resources\`" | grep -v -e "\`apiVersion\`" | grep -v -e "\`apiversion\`" | cut -d\` -f2
|
||||
cat out | grep "feature \`" | grep -v "\`resources\`" | grep -v -e "\`apiVersion\`" | grep -v -e "\`apiversion\`" | awk '{print $6}'
|
||||
1
api/internal/crawl/config/crawler/base/.gitignore
vendored
Normal file
1
api/internal/crawl/config/crawler/base/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
github_api_secret.txt
|
||||
@@ -11,13 +11,8 @@ spec:
|
||||
image: gcr.io/haiyanmeng-gke-dev/kustomize_stats:v1
|
||||
imagePullPolicy: Always
|
||||
command: ["/kustomize_stats"]
|
||||
args: ["--index=kustomize", "--kinds=50", "--identifiers=50", "--kustomize-features=50"]
|
||||
args: ["--index=kustomize", "--kinds=51", "--identifiers=50", "--kustomize-features=50"]
|
||||
env:
|
||||
- name: GITHUB_ACCESS_TOKEN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: github-access-token
|
||||
key: token
|
||||
- name: ELASTICSEARCH_URL
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
# ESBackup depends on ESCluster, and is depended by ESSnapshot.
|
||||
# Creating `esbackup/kustomize-backbup` will create the `kustomize-backup` snapshot repository.
|
||||
# Deleting `esbackup/kustomize-backbup` will delete the `kustomize-backup` snapshot repository and all the snapshots in the repository.
|
||||
# Deleting `esbackup/kustomize-backbup` will NOT delete essnapshot and esrestore objects.
|
||||
# Deleting `esbackup/kustomize-backbup` will delete the `kustomize-backup` snapshot repository.
|
||||
# Deleting `esbackup/kustomize-backbup` will NOT delete the snapshots in the `kustomize-backup` snapshot repository, instead makes all the snapshots in the repository inaccessible.
|
||||
# Deleting `esbackup/kustomize-backbup` will NOT delete the essnapshot objects depending on it, but will cause those essnapshot objects to be reconciled, which update the status of the essnapshot objects to reflect the fact that the esbackup object is missing.
|
||||
# If you delete the `kustomize-backup` snapshot repository directly without deleting `esbackup/kustomize-backbup`, the ESBackup object will not recreate the snapshot repository.
|
||||
apiVersion: elasticsearch.cloud.google.com/v1alpha1
|
||||
kind: ESBackup
|
||||
metadata:
|
||||
@@ -8,7 +11,11 @@ metadata:
|
||||
spec:
|
||||
storage:
|
||||
gcs:
|
||||
# the bucket must exist for the snapshot respository to be created successfully.
|
||||
bucket: kustomize-backup
|
||||
# the path does not need to exist.
|
||||
# If the path does not exist, the controller will create the folder in the GCS bucket.
|
||||
# If the path already exists and includes snapshots, these snapshots can be used.
|
||||
path: kustomize
|
||||
secret:
|
||||
name: kustomizesa
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# ESCluster is depended by ESBackup and ESRestore.
|
||||
apiVersion: elasticsearch.cloud.google.com/v1alpha1
|
||||
kind: ESCluster
|
||||
metadata:
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
# ESRestore depends on both ESCluster and ESSnapshot.
|
||||
# Creating `esrestore/kustomize-restore` will restore the `kuostmize` index in the `kustomize-snapshot` snapshot to a new index named `kusotmize-restore`.
|
||||
# Deleting `esrestore/kustomize-restore` will not delete the restored index.
|
||||
# Deleting `esrestore/kustomize-restore` should happen before deleting `essnapshot/kustomize-snapshot`.
|
||||
# After the restore is complete, if the `kusotmize-restore` index is deleted manually, the ESRestore object will NOT restore the `kustomize` index to it again.
|
||||
# The correct way of using ESRestore is: create a ESRestore object to restore the index; delete the ESRestore object after the restore is complete.
|
||||
apiVersion: elasticsearch.cloud.google.com/v1alpha1
|
||||
kind: ESRestore
|
||||
metadata:
|
||||
|
||||
23
api/internal/crawl/config/elastic/essnapshot.yaml
Normal file
23
api/internal/crawl/config/elastic/essnapshot.yaml
Normal file
@@ -0,0 +1,23 @@
|
||||
# ESSnapshot depends on ESBackup, and is depended by ESRestore.
|
||||
# Creating `essnapshot/kustomize-snapshot` will create a snapshot named `kustomize-snapshot` in the `kustomize-backup` snapshot repository.
|
||||
# After being created, the `kustomize-snapshot` snapshot will not be automatically updated when the `kuostomize` index is updated.
|
||||
# If you delete `essnapshot/kustomize-snapshot` and recreate it, the new snapshot will capture the current status of the `kustomize` index.
|
||||
# Deleting `essnapshot/kustomize-snapshot` will delete the snapshot.
|
||||
# Deleting `essnapshot/kustomize-snapshot` should happen before deleting `esbackup/kustomize-backup`.
|
||||
# If the `kustomize-snapshot` snapshot is deleted directly without deleting `essnapshot/kustomize-snapshot`, the ESSnapshot object will recreate the snapshot.
|
||||
# The correct way of using ESSnapshot is: create an ESSnapshot object to create a snapshot, keep the ESSnapshot object until the snapshot is no longer needed.
|
||||
# To update the snapshot to capture the latest version of the index, you can either:
|
||||
# 1) delete the snapshot, and wait for the ESSnapshot object to recreate the snapshot;
|
||||
# 2) delete the ESSnapshot object, and recreate the ESSnapshot object.
|
||||
apiVersion: elasticsearch.cloud.google.com/v1alpha1
|
||||
kind: ESSnapshot
|
||||
metadata:
|
||||
name: kustomize-snapshot
|
||||
spec:
|
||||
# indices are optional. If not specified all indices are selected.
|
||||
indices:
|
||||
- kustomize
|
||||
include_global_state: true
|
||||
ignore_unavailable: true
|
||||
esbackup:
|
||||
name: kustomize-backup
|
||||
@@ -1,15 +0,0 @@
|
||||
# Creating `essnapshot/kustomize-snapshot` will create a snapshot named `kustomize-snapshot` in the `kustomize-backup` snapshot repository.
|
||||
# Deleting `essnapshot/kustomize-snapshot` will delete the snapshot.
|
||||
# Deleting `essnapshot/kustomize-snapshot` should happen before deleting `esbackup/kustomize-backup`.
|
||||
apiVersion: elasticsearch.cloud.google.com/v1alpha1
|
||||
kind: ESSnapshot
|
||||
metadata:
|
||||
name: kustomize-snapshot
|
||||
spec:
|
||||
# indices are optional. If not specified all indices are selected.
|
||||
indices:
|
||||
- kustomize
|
||||
include_global_state: true
|
||||
ignore_unavailable: true
|
||||
esbackup:
|
||||
name: kustomize-backup
|
||||
@@ -1,6 +1,6 @@
|
||||
Find out the largest value of the `creationTime` field:
|
||||
```
|
||||
curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"aggs" : {
|
||||
"max_creationTime" : { "max" : { "field" : "creationTime" } }
|
||||
@@ -11,7 +11,7 @@ curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'C
|
||||
|
||||
Find out the smallest value of the `creationTime` field:
|
||||
```
|
||||
curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"aggs" : {
|
||||
"min_creationTime" : { "min" : { "field" : "creationTime" } }
|
||||
@@ -22,7 +22,7 @@ curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'C
|
||||
|
||||
Find out the smallest value of the `creationTime` field of all the kustomization files:
|
||||
```
|
||||
curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -40,7 +40,7 @@ curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'C
|
||||
|
||||
Find out the smallest value of the `creationTime` field of all kustomize resource files:
|
||||
```
|
||||
curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -61,7 +61,7 @@ curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'C
|
||||
|
||||
Find out the smallest value of the `creationTime` field of all kustomize generator files:
|
||||
```
|
||||
curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -82,7 +82,7 @@ curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'C
|
||||
|
||||
Find out the smallest value of the `creationTime` field of all kustomize transformer files:
|
||||
```
|
||||
curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -103,7 +103,7 @@ curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'C
|
||||
|
||||
Query all the documents whose `creationTime` <= `2016-07-29T17:38:26.000Z`:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"range": {
|
||||
@@ -118,7 +118,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
|
||||
Query all the documents whose `creationTime` falls within the specific range:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"range": {
|
||||
@@ -134,7 +134,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
|
||||
Query all the kustomization files whose `creationTime` falls within the specific range:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"size": 20,
|
||||
"query": {
|
||||
@@ -158,7 +158,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
|
||||
Aggregate how many new kustomization files were added into Github each month:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -181,7 +181,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Co
|
||||
|
||||
Aggregate how many new kustomize resource files were added into Github each month:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -207,7 +207,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Co
|
||||
|
||||
Aggregate how many new kustomize generator files were added into Github each month:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -233,7 +233,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Co
|
||||
|
||||
Aggregate how many new kustomize transformer files were added into Github each month:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -259,7 +259,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Co
|
||||
|
||||
Aggregate how many new kustomization files were added into Github each year:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -282,7 +282,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Co
|
||||
|
||||
Aggregate how many new kustomize resource files were added into Github each year:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -308,7 +308,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Co
|
||||
|
||||
Aggregate how many new kustomize generator files were added into Github each year:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -334,7 +334,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Co
|
||||
|
||||
Aggregate how many new kustomize transformer files were added into Github each year:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -360,7 +360,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Co
|
||||
|
||||
Find the generator files created within the given time range:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -386,7 +386,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
|
||||
Find the transformer files created within the given time range:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Count distinct values of the `defaultBranch` field:
|
||||
```
|
||||
curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"aggs" : {
|
||||
"defaultBranch_count" : {
|
||||
@@ -17,7 +17,7 @@ curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'C
|
||||
List all the github branches where kustomization files and kustomize resource files live,
|
||||
and how many kustomization files and kustomize resource files live in each branch:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"aggs" : {
|
||||
"defaultBranch" : {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Count the documents whose `document` field is empty (The reason why the `document` field
|
||||
of a document is empty is because of empty documents):
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"size": 10000,
|
||||
"query": {
|
||||
@@ -19,7 +19,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
|
||||
Find all the documents having the `creationTime` field set:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"exists": {
|
||||
@@ -32,7 +32,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
|
||||
Find all the documents whose `creationTime` field is not set:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"size": 10000,
|
||||
"query": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Find all the documents having the `fileType` field set:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"exists": {
|
||||
@@ -13,7 +13,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
|
||||
Find all the documents whose `fileType` field is not set:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"size": 10000,
|
||||
"query": {
|
||||
@@ -31,7 +31,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
|
||||
Search for all the documents whose `fileType` field is `resource`:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -46,7 +46,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
|
||||
Search for all the kustomization files whose `fileType` field is `resource`:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -62,7 +62,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
|
||||
Search for all the kustomize resource files:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -80,7 +80,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
|
||||
Search all the kustomization files including a `generators` field:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"size": 10000,
|
||||
"query": {
|
||||
@@ -103,7 +103,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
|
||||
Search for all the documents whose `fileType` field is `generator`:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -118,7 +118,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
|
||||
Search for all the kustomization files whose `fileType` field is `generator`:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -134,7 +134,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
|
||||
Search for all the kustomize generator files:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -152,7 +152,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
|
||||
Search all the kustomization files including a `transformers` field:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"size": 10000,
|
||||
"query": {
|
||||
@@ -175,7 +175,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
|
||||
Search for all the documents whose `fileType` field is `transformer`:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -190,7 +190,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
|
||||
Search for all the kustomization files whose `fileType` field is `transformer`:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -206,7 +206,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
|
||||
Search for all the kustomize transformer files:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -224,7 +224,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
|
||||
Count distinct values of the `fileType` field:
|
||||
```
|
||||
curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"aggs" : {
|
||||
"fileType_count" : {
|
||||
@@ -240,7 +240,7 @@ curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'C
|
||||
|
||||
List all the values of the `fileType` field and the frequency of each value:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"aggs" : {
|
||||
"fileType" : {
|
||||
@@ -257,7 +257,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Co
|
||||
For all the kustomization files in the index, list all the values of the
|
||||
`fileType` field and the frequency of each value:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -280,7 +280,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Co
|
||||
For all the non-kustomization files in the index, list all the values of the
|
||||
`fileType` field and the frequency of each value:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Find all the generator files whose `kinds` field includes `ChartRenderer`, and
|
||||
only output certain fields of each document:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"size": 200,
|
||||
"_source": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Find the document with the given `_id`:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"terms": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Count the documents in the index whose `repositoryUrl` field starts with
|
||||
`https://github.com/`:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -17,7 +17,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
Count the documents in the index whose `repositoryUrl` field does not start with
|
||||
`https://github.com/`:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -33,7 +33,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
Search all the documents matching the given `repositoryUrl` and `filePath`, and return
|
||||
a version for each search hit:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"size": 10000,
|
||||
"version": true,
|
||||
@@ -52,7 +52,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
Search all the documents whose filePath ends with one of these following three filenames:
|
||||
`kustomization.yaml`, `kustomization.yml`, `kustomization`:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -68,7 +68,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
Search all the documents whose filePath does not end with any of these following
|
||||
three filenames: `kustomization.yaml`, `kustomization.yml`, `kustomization`:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
Check the health status of an ElasticSearch cluster:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/_cat/health?v&pretty"
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/_cat/health?v&pretty"
|
||||
```
|
||||
|
||||
Check the indices in an ElasticSearch cluster:
|
||||
```
|
||||
curl "${ElasticSearchURL}:9200/_cat/indices?v"
|
||||
curl -s "${ElasticSearchURL}:9200/_cat/indices?v"
|
||||
```
|
||||
|
||||
Get the mapping of the index:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_mapping?pretty"
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_mapping?pretty"
|
||||
```
|
||||
|
||||
Delete the kustomize index from the ElasticSearch cluster (**Use this command with caution**):
|
||||
```
|
||||
curl -X DELETE "${ElasticSearchURL}:9200/${INDEXNAME}?pretty"
|
||||
curl -s -X DELETE "${ElasticSearchURL}:9200/${INDEXNAME}?pretty"
|
||||
```
|
||||
|
||||
Add a new field into an existing index.
|
||||
```
|
||||
curl -X PUT "${ElasticSearchURL}:9200/${INDEXNAME}/_mapping/_doc?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X PUT "${ElasticSearchURL}:9200/${INDEXNAME}/_mapping/_doc?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"properties": {
|
||||
"fileType": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Count distinct values of the `repositoryUrl` field:
|
||||
```
|
||||
curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"aggs" : {
|
||||
"repositoryUrl_count" : {
|
||||
@@ -16,7 +16,7 @@ curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'C
|
||||
|
||||
Count how many Github repositories include kustomization files:
|
||||
```
|
||||
curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -39,7 +39,7 @@ curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'C
|
||||
|
||||
Count distinct values of the `repositoryUrl` field for all the kustomize resource files in the index:
|
||||
```
|
||||
curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -65,7 +65,7 @@ curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'C
|
||||
|
||||
Count distinct values of the `repositoryUrl` field for all the kustomize generator files in the index:
|
||||
```
|
||||
curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -91,7 +91,7 @@ curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'C
|
||||
|
||||
Count distinct values of the `repositoryUrl` field for all the kustomize transformer files in the index:
|
||||
```
|
||||
curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -117,7 +117,7 @@ curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'C
|
||||
|
||||
Count distinct values of the `repositoryUrl` field for all the kustomize resource dirs in the index:
|
||||
```
|
||||
curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -141,7 +141,7 @@ curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'C
|
||||
|
||||
Count distinct values of the `repositoryUrl` field for all the kustomize generator dirs in the index:
|
||||
```
|
||||
curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -165,7 +165,7 @@ curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'C
|
||||
|
||||
Count distinct values of the `repositoryUrl` field for all the kustomize transformer dirs in the index:
|
||||
```
|
||||
curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -191,7 +191,7 @@ List all the github repositories including kustomization files and kustomize res
|
||||
and how many kustomization files and kustomize resource files each github repository includes
|
||||
(the github repository including the most kustomization files is listed first):
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"aggs" : {
|
||||
"repositoryUrl" : {
|
||||
@@ -207,7 +207,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Co
|
||||
|
||||
List the top 20 Github repositories including the most amount of kustomization files:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -230,7 +230,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Co
|
||||
|
||||
List the top 20 Github repositories including the most amount of kustomize resource files:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
Retrieve information about all registered snapshot repositories:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/_snapshot?pretty"
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/_snapshot?pretty"
|
||||
```
|
||||
|
||||
Retrieve information about a given snapshot repository, `kustomize-backup`:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/_snapshot/kustomize-backup?pretty"
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/_snapshot/kustomize-backup?pretty"
|
||||
```
|
||||
|
||||
Verify a snapshot repository, `kustomize-backup`, manually:
|
||||
```
|
||||
curl -X POST "${ElasticSearchURL}:9200/_snapshot/kustomize-backup/_verify?pretty"
|
||||
curl -s -X POST "${ElasticSearchURL}:9200/_snapshot/kustomize-backup/_verify?pretty"
|
||||
```
|
||||
|
||||
List all the snapshots in a given snapshot repository:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/_cat/snapshots/kustomize-backup?v&s=id&pretty"
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/_cat/snapshots/kustomize-backup?v&s=id&pretty"
|
||||
```
|
||||
|
||||
Retrieve a summary information about a given snapshot:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/_snapshot/kustomize-backup/kustomize-snapshot?pretty"
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/_snapshot/kustomize-backup/kustomize-snapshot?pretty"
|
||||
```
|
||||
|
||||
Retrieve a detailed information about a given snapshot:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/_snapshot/kustomize-backup/kustomize-snapshot/_status?pretty"
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/_snapshot/kustomize-backup/kustomize-snapshot/_status?pretty"
|
||||
```
|
||||
|
||||
1090
api/internal/crawl/search_cmds/stats.md
Normal file
1090
api/internal/crawl/search_cmds/stats.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
Search for all the kustomize resource files including a Deployment object:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"match" : {
|
||||
@@ -16,7 +16,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
Search for all the kustomize resource files including a Deployment object, but only
|
||||
including the `kinds` field in the result:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"_source": {
|
||||
"includes": ["kinds"]
|
||||
@@ -35,7 +35,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
Search for all the kustomize resource files including both a Deployment object and
|
||||
a Service object:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"match" : {
|
||||
@@ -52,7 +52,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
Count the number of documents including Deployment and the number of documents
|
||||
including Service:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"size": 0,
|
||||
"aggs" : {
|
||||
@@ -71,7 +71,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
|
||||
Search for all the kustomization files involving CRDs:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"size": 10000,
|
||||
"query": {
|
||||
@@ -87,7 +87,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
|
||||
Search for all the kustomization files defining configMapGenerator:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"size": 10000,
|
||||
"query": {
|
||||
@@ -103,7 +103,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
|
||||
Search for all the documents having a `kind` field:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -118,7 +118,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
|
||||
Search for all the kuostmization files having a `kind` field:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -134,7 +134,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
|
||||
Search for all the kustomization files defining the `generatorOptions:disableNameSuffixHash` feature:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"match" : {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Find all the trasnformer files whose `kinds` field includes `HelmValues`, and
|
||||
only output certain fields of each document:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"size": 200,
|
||||
"_source": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Find all the documents having the `user` field set:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"exists": {
|
||||
@@ -13,7 +13,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
|
||||
Find all the documents whose `user` field is not set:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"size": 10000,
|
||||
"query": {
|
||||
@@ -31,7 +31,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
|
||||
Search for all the documents whose `user` field is `kubernetes-sigs`:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -46,7 +46,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?pretty" -H 'Content-T
|
||||
|
||||
Count distinct values of the `user` field:
|
||||
```
|
||||
curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"aggs" : {
|
||||
"user_count" : {
|
||||
@@ -62,7 +62,7 @@ curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'C
|
||||
|
||||
List all the values of the `user` field and the frequency of each value:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"aggs" : {
|
||||
"user" : {
|
||||
@@ -78,7 +78,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Co
|
||||
|
||||
Count distinct values of the `user` field for all the kustomization files in the index:
|
||||
```
|
||||
curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -102,7 +102,7 @@ curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'C
|
||||
For all the kustomization files in the index, list all the values of the
|
||||
`user` field and the frequency of each value:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -125,7 +125,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Co
|
||||
|
||||
Count distinct values of the `user` field for all the kustomize resource files in the index:
|
||||
```
|
||||
curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -152,7 +152,7 @@ curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'C
|
||||
For all the kustomize resource files in the index, list all the values of the
|
||||
`user` field and the frequency of each value:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -178,7 +178,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Co
|
||||
|
||||
Count distinct values of the `user` field for all the kustomize generator files in the index:
|
||||
```
|
||||
curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -205,7 +205,7 @@ curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'C
|
||||
For all the kustomize generator files in the index, list all the values of the
|
||||
`user` field and the frequency of each value:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -231,7 +231,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Co
|
||||
|
||||
Count distinct values of the `user` field for all the kustomize transformer files in the index:
|
||||
```
|
||||
curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -258,7 +258,7 @@ curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'C
|
||||
For all the kustomize transformer files in the index, list all the values of the
|
||||
`user` field and the frequency of each value:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -284,7 +284,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Co
|
||||
|
||||
Count distinct values of the `user` field for all the kustomize generator dirs in the index:
|
||||
```
|
||||
curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -309,7 +309,7 @@ curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'C
|
||||
For all the kustomize generator dirs in the index, list all the values of the
|
||||
`user` field and the frequency of each value:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -333,7 +333,7 @@ curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Co
|
||||
|
||||
Count distinct values of the `user` field for all the kustomize transformer dirs in the index:
|
||||
```
|
||||
curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
@@ -358,7 +358,7 @@ curl -X POST "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'C
|
||||
For all the kustomize transformer dirs in the index, list all the values of the
|
||||
`user` field and the frequency of each value:
|
||||
```
|
||||
curl -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
curl -s -X GET "${ElasticSearchURL}:9200/${INDEXNAME}/_search?size=0&pretty" -H 'Content-Type: application/json' -d'
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
|
||||
@@ -45,22 +45,23 @@ Advanced Documentation Topics:
|
||||
|
||||
// Export commands publicly for composition
|
||||
var (
|
||||
Annotate = commands.AnnotateCommand
|
||||
Cat = commands.CatCommand
|
||||
Count = commands.CountCommand
|
||||
CreateSetter = commands.CreateSetterCommand
|
||||
Fmt = commands.FmtCommand
|
||||
Grep = commands.GrepCommand
|
||||
ListSetters = commands.ListSettersCommand
|
||||
Merge = commands.MergeCommand
|
||||
Merge3 = commands.Merge3Command
|
||||
RunFn = commands.RunFnCommand
|
||||
Set = commands.SetCommand
|
||||
Sink = commands.SinkCommand
|
||||
Source = commands.SourceCommand
|
||||
Tree = commands.TreeCommand
|
||||
Wrap = commands.WrapCommand
|
||||
XArgs = commands.XArgsCommand
|
||||
Annotate = commands.AnnotateCommand
|
||||
Cat = commands.CatCommand
|
||||
Count = commands.CountCommand
|
||||
CreateSetter = commands.CreateSetterCommand
|
||||
CreateSubstitution = commands.CreateSubstitutionCommand
|
||||
Fmt = commands.FmtCommand
|
||||
Grep = commands.GrepCommand
|
||||
ListSetters = commands.ListSettersCommand
|
||||
Merge = commands.MergeCommand
|
||||
Merge3 = commands.Merge3Command
|
||||
RunFn = commands.RunFnCommand
|
||||
Set = commands.SetCommand
|
||||
Sink = commands.SinkCommand
|
||||
Source = commands.SourceCommand
|
||||
Tree = commands.TreeCommand
|
||||
Wrap = commands.WrapCommand
|
||||
XArgs = commands.XArgsCommand
|
||||
|
||||
StackOnError = &commands.StackOnError
|
||||
ExitOnError = &commands.ExitOnError
|
||||
@@ -107,6 +108,7 @@ func NewConfigCommand(name string) *cobra.Command {
|
||||
root.AddCommand(commands.SetCommand(name))
|
||||
root.AddCommand(commands.ListSettersCommand(name))
|
||||
root.AddCommand(commands.CreateSetterCommand(name))
|
||||
root.AddCommand(commands.CreateSubstitutionCommand(name))
|
||||
root.AddCommand(commands.SinkCommand(name))
|
||||
root.AddCommand(commands.SourceCommand(name))
|
||||
|
||||
|
||||
12
cmd/config/ext/ext.go
Normal file
12
cmd/config/ext/ext.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package ext
|
||||
|
||||
import "path/filepath"
|
||||
|
||||
// GetOpenAPIFile returns the path to the file containing supplementary OpenAPI definitions.
|
||||
// Maybe be overridden to configure which file to read OpenAPI definitions from.
|
||||
var GetOpenAPIFile = func(args []string) (string, error) {
|
||||
return filepath.Join(args[0], "kustomization"), nil
|
||||
}
|
||||
@@ -5,9 +5,11 @@ package commands
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
|
||||
)
|
||||
|
||||
// NewCreateSetterRunner returns a command runner.
|
||||
@@ -23,21 +25,28 @@ func NewCreateSetterRunner(parent string) *CreateSetterRunner {
|
||||
RunE: r.runE,
|
||||
}
|
||||
set.Flags().StringVar(&r.Set.SetPartialField.SetBy, "set-by", "",
|
||||
"set the setBy annotation.")
|
||||
"record who the field was default by.")
|
||||
set.Flags().StringVar(&r.Set.SetPartialField.Description, "description", "",
|
||||
"set the description of the field value.")
|
||||
"record a description for the current setter value.")
|
||||
set.Flags().StringVar(&r.Set.SetPartialField.Field, "field", "",
|
||||
"name of the field to set -- e.g. --field port")
|
||||
"name of the field to set -- e.g. --field port. defaults to all fields match"+
|
||||
"VALUE. maybe be the field name, field path, or partial field path (suffix)")
|
||||
set.Flags().StringVar(&r.Set.ResourceMeta.Name, "name", "",
|
||||
"name of the Resource on which to create the setter.")
|
||||
set.Flags().MarkHidden("name")
|
||||
set.Flags().StringVar(&r.Set.ResourceMeta.Kind, "kind", "",
|
||||
"kind of the Resource on which to create the setter.")
|
||||
set.Flags().MarkHidden("kind")
|
||||
set.Flags().StringVar(&r.Set.SetPartialField.Type, "type", "",
|
||||
"valid OpenAPI field type -- e.g. integer,boolean,string.")
|
||||
set.Flags().MarkHidden("type")
|
||||
set.Flags().BoolVar(&r.Set.SetPartialField.Partial, "partial", false,
|
||||
"create a partial setter for only part of the field value.")
|
||||
set.Flags().MarkHidden("partial")
|
||||
set.Flags().StringVar(&setterVersion, "version", "",
|
||||
"use this version of the setter format")
|
||||
set.Flags().MarkHidden("version")
|
||||
fixDocs(parent, set)
|
||||
set.MarkFlagRequired("type")
|
||||
r.Command = set
|
||||
return r
|
||||
}
|
||||
@@ -47,8 +56,10 @@ func CreateSetterCommand(parent string) *cobra.Command {
|
||||
}
|
||||
|
||||
type CreateSetterRunner struct {
|
||||
Command *cobra.Command
|
||||
Set setters.CreateSetter
|
||||
Command *cobra.Command
|
||||
Set setters.CreateSetter
|
||||
CreateSetter settersutil.SetterCreator
|
||||
OpenAPIFile string
|
||||
}
|
||||
|
||||
func (r *CreateSetterRunner) runE(c *cobra.Command, args []string) error {
|
||||
@@ -56,12 +67,40 @@ func (r *CreateSetterRunner) runE(c *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
func (r *CreateSetterRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
var err error
|
||||
r.Set.SetPartialField.Setter.Name = args[1]
|
||||
r.Set.SetPartialField.Setter.Value = args[2]
|
||||
r.CreateSetter.Name = args[1]
|
||||
r.CreateSetter.FieldValue = args[2]
|
||||
r.CreateSetter.FieldName, err = c.Flags().GetString("field")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if setterVersion == "" {
|
||||
if len(args) < 3 {
|
||||
setterVersion = "v1"
|
||||
} else if err := initSetterVersion(c, args); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if setterVersion == "v2" {
|
||||
var err error
|
||||
r.OpenAPIFile, err = ext.GetOpenAPIFile(args)
|
||||
r.CreateSetter.Description = r.Set.SetPartialField.Description
|
||||
r.CreateSetter.SetBy = r.Set.SetPartialField.SetBy
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *CreateSetterRunner) set(c *cobra.Command, args []string) error {
|
||||
if setterVersion == "v2" {
|
||||
return r.CreateSetter.Create(r.OpenAPIFile, args[0])
|
||||
}
|
||||
|
||||
rw := &kio.LocalPackageReadWriter{PackagePath: args[0]}
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{rw},
|
||||
|
||||
131
cmd/config/internal/commands/cmdcreatesetter_test.go
Normal file
131
cmd/config/internal/commands/cmdcreatesetter_test.go
Normal file
@@ -0,0 +1,131 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
)
|
||||
|
||||
func TestCreateSetterCommand(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
input string
|
||||
args []string
|
||||
out string
|
||||
expectedOpenAPI string
|
||||
expectedResources string
|
||||
}{
|
||||
{
|
||||
name: "add replicas",
|
||||
args: []string{"replicas", "3", "--description", "hello world", "--set-by", "me"},
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
`,
|
||||
expectedOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
description: hello world
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
setBy: me
|
||||
`,
|
||||
expectedResources: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`,
|
||||
},
|
||||
}
|
||||
for i := range tests {
|
||||
test := tests[i]
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
// reset the openAPI afterward
|
||||
openapi.ResetOpenAPI()
|
||||
defer openapi.ResetOpenAPI()
|
||||
|
||||
f, err := ioutil.TempFile("", "k8s-cli-")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
err = ioutil.WriteFile(f.Name(), []byte(`
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
`), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
old := ext.GetOpenAPIFile
|
||||
defer func() { ext.GetOpenAPIFile = old }()
|
||||
ext.GetOpenAPIFile = func(args []string) (s string, err error) {
|
||||
return f.Name(), nil
|
||||
}
|
||||
|
||||
r, err := ioutil.TempFile("", "k8s-cli-*.yaml")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
defer os.Remove(r.Name())
|
||||
err = ioutil.WriteFile(r.Name(), []byte(test.input), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
runner := commands.NewCreateSetterRunner("")
|
||||
out := &bytes.Buffer{}
|
||||
runner.Command.SetOut(out)
|
||||
runner.Command.SetArgs(append([]string{r.Name()}, test.args...))
|
||||
err = runner.Command.Execute()
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if !assert.Equal(t, test.out, out.String()) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
actualResources, err := ioutil.ReadFile(r.Name())
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(test.expectedResources),
|
||||
strings.TrimSpace(string(actualResources))) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
actualOpenAPI, err := ioutil.ReadFile(f.Name())
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(test.expectedOpenAPI),
|
||||
strings.TrimSpace(string(actualOpenAPI))) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
81
cmd/config/internal/commands/cmdcreatesubstitution.go
Normal file
81
cmd/config/internal/commands/cmdcreatesubstitution.go
Normal file
@@ -0,0 +1,81 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters2"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
|
||||
)
|
||||
|
||||
// NewCreateSubstitutionRunner returns a command runner.
|
||||
func NewCreateSubstitutionRunner(parent string) *CreateSubstitutionRunner {
|
||||
r := &CreateSubstitutionRunner{}
|
||||
cs := &cobra.Command{
|
||||
Use: "create-subst DIR NAME VALUE",
|
||||
Args: cobra.ExactArgs(3),
|
||||
PreRunE: r.preRunE,
|
||||
RunE: r.runE,
|
||||
}
|
||||
cs.Flags().StringVar(&r.CreateSubstitution.FieldName, "field", "",
|
||||
"name of the field to set -- e.g. --field port")
|
||||
cs.Flags().StringVar(&r.CreateSubstitution.Pattern, "pattern", "",
|
||||
"substitution pattern")
|
||||
cs.Flags().StringSliceVar(&r.Values, "value", []string{""},
|
||||
"substitution values for the pattern. format is PATTERN_MARKER=SETTER_NAME"+
|
||||
"where PATTERN_MARKER is the pattern substring to replace, and SETTER_NAME is the"+
|
||||
"setter from which to take the replacement value.")
|
||||
_ = cs.MarkFlagRequired("pattern")
|
||||
fixDocs(parent, cs)
|
||||
r.Command = cs
|
||||
return r
|
||||
}
|
||||
|
||||
func CreateSubstitutionCommand(parent string) *cobra.Command {
|
||||
return NewCreateSubstitutionRunner(parent).Command
|
||||
}
|
||||
|
||||
type CreateSubstitutionRunner struct {
|
||||
Command *cobra.Command
|
||||
CreateSubstitution settersutil.SubstitutionCreator
|
||||
OpenAPIFile string
|
||||
Values []string
|
||||
}
|
||||
|
||||
func (r *CreateSubstitutionRunner) runE(c *cobra.Command, args []string) error {
|
||||
return handleError(c, r.CreateSubstitution.Create(r.OpenAPIFile, args[0]))
|
||||
}
|
||||
|
||||
func (r *CreateSubstitutionRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
var err error
|
||||
r.CreateSubstitution.Name = args[1]
|
||||
r.CreateSubstitution.FieldValue = args[2]
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.OpenAPIFile, err = ext.GetOpenAPIFile(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// parse the marker values
|
||||
for i := range r.Values {
|
||||
parts := strings.SplitN(r.Values[i], "=", 2)
|
||||
if len(parts) < 2 {
|
||||
return errors.Errorf("values must be specified as PATTERN_MARKER=SETTER_NAME")
|
||||
}
|
||||
ref := setters2.DefinitionsPrefix + setters2.SetterDefinitionPrefix + parts[1]
|
||||
r.CreateSubstitution.Values = append(
|
||||
r.CreateSubstitution.Values,
|
||||
setters2.Value{Marker: parts[0], Ref: ref},
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
174
cmd/config/internal/commands/cmdcreatesubstitution_test.go
Normal file
174
cmd/config/internal/commands/cmdcreatesubstitution_test.go
Normal file
@@ -0,0 +1,174 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
)
|
||||
|
||||
func TestCreateSubstitutionCommand(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
inputOpenAPI string
|
||||
input string
|
||||
args []string
|
||||
out string
|
||||
expectedOpenAPI string
|
||||
expectedResources string
|
||||
}{
|
||||
{
|
||||
name: "substitution replicas",
|
||||
args: []string{
|
||||
"image", "nginx:1.7.9", "--pattern", "IMAGE:TAG",
|
||||
"--value", "IMAGE=image", "--value", "TAG=tag"},
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9
|
||||
- name: sidecar
|
||||
image: sidecar:1.7.9
|
||||
`,
|
||||
inputOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.image:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image
|
||||
value: "nginx"
|
||||
io.k8s.cli.setters.tag:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: tag
|
||||
value: "1.7.9"
|
||||
`,
|
||||
expectedOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.image:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image
|
||||
value: "nginx"
|
||||
io.k8s.cli.setters.tag:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: tag
|
||||
value: "1.7.9"
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: IMAGE:TAG
|
||||
values:
|
||||
- marker: IMAGE
|
||||
ref: '#/definitions/io.k8s.cli.setters.image'
|
||||
- marker: TAG
|
||||
ref: '#/definitions/io.k8s.cli.setters.tag'
|
||||
`,
|
||||
expectedResources: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref":"#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: sidecar
|
||||
image: sidecar:1.7.9
|
||||
`,
|
||||
},
|
||||
}
|
||||
for i := range tests {
|
||||
test := tests[i]
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
// reset the openAPI afterward
|
||||
openapi.ResetOpenAPI()
|
||||
defer openapi.ResetOpenAPI()
|
||||
|
||||
f, err := ioutil.TempFile("", "k8s-cli-")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
err = ioutil.WriteFile(f.Name(), []byte(test.inputOpenAPI), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
old := ext.GetOpenAPIFile
|
||||
defer func() { ext.GetOpenAPIFile = old }()
|
||||
ext.GetOpenAPIFile = func(args []string) (s string, err error) {
|
||||
return f.Name(), nil
|
||||
}
|
||||
|
||||
r, err := ioutil.TempFile("", "k8s-cli-*.yaml")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
defer os.Remove(r.Name())
|
||||
err = ioutil.WriteFile(r.Name(), []byte(test.input), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
runner := commands.NewCreateSubstitutionRunner("")
|
||||
out := &bytes.Buffer{}
|
||||
runner.Command.SetOut(out)
|
||||
runner.Command.SetArgs(append([]string{r.Name()}, test.args...))
|
||||
err = runner.Command.Execute()
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if !assert.Equal(t, test.out, out.String()) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
actualResources, err := ioutil.ReadFile(r.Name())
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(test.expectedResources),
|
||||
strings.TrimSpace(string(actualResources))) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
actualOpenAPI, err := ioutil.ReadFile(f.Name())
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(test.expectedOpenAPI),
|
||||
strings.TrimSpace(string(actualOpenAPI))) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,16 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters2"
|
||||
)
|
||||
|
||||
// NewListSettersRunner returns a command runner.
|
||||
@@ -33,15 +40,57 @@ func ListSettersCommand(parent string) *cobra.Command {
|
||||
type ListSettersRunner struct {
|
||||
Command *cobra.Command
|
||||
Lookup setters.LookupSetters
|
||||
List setters2.List
|
||||
}
|
||||
|
||||
func (r *ListSettersRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
if len(args) > 1 {
|
||||
r.Lookup.Name = args[1]
|
||||
r.List.Name = args[1]
|
||||
}
|
||||
|
||||
initSetterVersion(c, args)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ListSettersRunner) runE(c *cobra.Command, args []string) error {
|
||||
if setterVersion == "v2" {
|
||||
// use setters v2
|
||||
path, err := ext.GetOpenAPIFile(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.List.List(path, args[0]); err != nil {
|
||||
return err
|
||||
}
|
||||
table := newTable(c.OutOrStdout())
|
||||
table.SetHeader([]string{"NAME", "VALUE", "SET BY", "DESCRIPTION", "COUNT"})
|
||||
for i := range r.List.Setters {
|
||||
s := r.List.Setters[i]
|
||||
table.Append([]string{
|
||||
s.Name, s.Value, s.SetBy, s.Description, fmt.Sprintf("%d", s.Count)})
|
||||
}
|
||||
table.Render()
|
||||
|
||||
if len(r.List.Setters) == 0 {
|
||||
// exit non-0 if no matching setters are found
|
||||
if ExitOnError {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return handleError(c, lookup(r.Lookup, c, args))
|
||||
}
|
||||
|
||||
func newTable(o io.Writer) *tablewriter.Table {
|
||||
table := tablewriter.NewWriter(o)
|
||||
table.SetRowLine(false)
|
||||
table.SetBorder(false)
|
||||
table.SetHeaderLine(false)
|
||||
table.SetColumnSeparator(" ")
|
||||
table.SetCenterSeparator(" ")
|
||||
table.SetAlignment(tablewriter.ALIGN_LEFT)
|
||||
return table
|
||||
}
|
||||
|
||||
299
cmd/config/internal/commands/cmdlistsetters_test.go
Normal file
299
cmd/config/internal/commands/cmdlistsetters_test.go
Normal file
@@ -0,0 +1,299 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
)
|
||||
|
||||
func TestListSettersCommand(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
openapi string
|
||||
input string
|
||||
args []string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "list-replicas",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
setBy: me
|
||||
description: "hello world"
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`,
|
||||
expected: ` NAME VALUE SET BY DESCRIPTION COUNT
|
||||
replicas 3 me hello world 1
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "list-multiple",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
description: "hello world 1"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
setBy: me1
|
||||
io.k8s.cli.setters.image:
|
||||
description: "hello world 2"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image
|
||||
value: "nginx"
|
||||
setBy: me2
|
||||
io.k8s.cli.setters.tag:
|
||||
description: "hello world 3"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: tag
|
||||
value: "1.7.9"
|
||||
setBy: me3
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: IMAGE:TAG
|
||||
values:
|
||||
- marker: IMAGE
|
||||
ref: '#/definitions/io.k8s.cli.setters.image'
|
||||
- marker: TAG
|
||||
ref: '#/definitions/io.k8s.cli.setters.tag'
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: nginx2
|
||||
image: nginx # {"$ref": "#/definitions/io.k8s.cli.setters.image"}
|
||||
`,
|
||||
expected: ` NAME VALUE SET BY DESCRIPTION COUNT
|
||||
image nginx me2 hello world 2 2
|
||||
replicas 3 me1 hello world 1 1
|
||||
tag 1.7.9 me3 hello world 3 1
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "list-multiple-resources",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
description: "hello world 1"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
setBy: me1
|
||||
io.k8s.cli.setters.image:
|
||||
description: "hello world 2"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image
|
||||
value: "nginx"
|
||||
setBy: me2
|
||||
io.k8s.cli.setters.tag:
|
||||
description: "hello world 3"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: tag
|
||||
value: "1.7.9"
|
||||
setBy: me3
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: IMAGE:TAG
|
||||
values:
|
||||
- marker: IMAGE
|
||||
ref: '#/definitions/io.k8s.cli.setters.image'
|
||||
- marker: TAG
|
||||
ref: '#/definitions/io.k8s.cli.setters.tag'
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment-1
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: nginx2
|
||||
image: nginx # {"$ref": "#/definitions/io.k8s.cli.setters.image"}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment-2
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: nginx2
|
||||
image: nginx
|
||||
`,
|
||||
expected: ` NAME VALUE SET BY DESCRIPTION COUNT
|
||||
image nginx me2 hello world 2 3
|
||||
replicas 3 me1 hello world 1 2
|
||||
tag 1.7.9 me3 hello world 3 2
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "list-name",
|
||||
args: []string{"image"},
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
description: "hello world 1"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
setBy: me1
|
||||
io.k8s.cli.setters.image:
|
||||
description: "hello world 2"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image
|
||||
value: "nginx"
|
||||
setBy: me2
|
||||
io.k8s.cli.setters.tag:
|
||||
description: "hello world 3"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: tag
|
||||
value: "1.7.9"
|
||||
setBy: me3
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: IMAGE:TAG
|
||||
values:
|
||||
- marker: IMAGE
|
||||
ref: '#/definitions/io.k8s.cli.setters.image'
|
||||
- marker: TAG
|
||||
ref: '#/definitions/io.k8s.cli.setters.tag'
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment-1
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: nginx2
|
||||
image: nginx # {"$ref": "#/definitions/io.k8s.cli.setters.image"}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment-2
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: nginx2
|
||||
image: nginx
|
||||
`,
|
||||
expected: ` NAME VALUE SET BY DESCRIPTION COUNT
|
||||
image nginx me2 hello world 2 3
|
||||
`,
|
||||
},
|
||||
}
|
||||
for i := range tests {
|
||||
test := tests[i]
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
// reset the openAPI afterward
|
||||
openapi.ResetOpenAPI()
|
||||
defer openapi.ResetOpenAPI()
|
||||
|
||||
f, err := ioutil.TempFile("", "k8s-cli-")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
old := ext.GetOpenAPIFile
|
||||
defer func() { ext.GetOpenAPIFile = old }()
|
||||
ext.GetOpenAPIFile = func(args []string) (s string, err error) {
|
||||
err = ioutil.WriteFile(f.Name(), []byte(test.openapi), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
return f.Name(), nil
|
||||
}
|
||||
|
||||
r, err := ioutil.TempFile("", "k8s-cli-*.yaml")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
defer os.Remove(r.Name())
|
||||
err = ioutil.WriteFile(r.Name(), []byte(test.input), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
runner := commands.NewListSettersRunner("")
|
||||
actual := &bytes.Buffer{}
|
||||
runner.Command.SetOut(actual)
|
||||
runner.Command.SetArgs(append([]string{r.Name()}, test.args...))
|
||||
err = runner.Command.Execute()
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if !assert.Equal(t, test.expected, actual.String()) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,18 @@ import (
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
|
||||
)
|
||||
|
||||
// NewSetRunner returns a command runner.
|
||||
func NewSetRunner(parent string) *SetRunner {
|
||||
r := &SetRunner{}
|
||||
c := &cobra.Command{
|
||||
Use: "set DIR [NAME] [VALUE]",
|
||||
Use: "set DIR NAME [VALUE]",
|
||||
Args: cobra.RangeArgs(1, 3),
|
||||
Short: commands.SetShort,
|
||||
Long: commands.SetLong,
|
||||
@@ -32,18 +34,44 @@ func NewSetRunner(parent string) *SetRunner {
|
||||
"annotate the field with who set it")
|
||||
c.Flags().StringVar(&r.Perform.Description, "description", "",
|
||||
"annotate the field with a description of its value")
|
||||
c.Flags().StringVar(&setterVersion, "version", "",
|
||||
"use this version of the setter format")
|
||||
c.Flags().MarkHidden("version")
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
var setterVersion string
|
||||
|
||||
func SetCommand(parent string) *cobra.Command {
|
||||
return NewSetRunner(parent).Command
|
||||
}
|
||||
|
||||
type SetRunner struct {
|
||||
Command *cobra.Command
|
||||
Lookup setters.LookupSetters
|
||||
Perform setters.PerformSetters
|
||||
Command *cobra.Command
|
||||
Lookup setters.LookupSetters
|
||||
Perform setters.PerformSetters
|
||||
Set settersutil.FieldSetter
|
||||
OpenAPIFile string
|
||||
}
|
||||
|
||||
func initSetterVersion(c *cobra.Command, args []string) error {
|
||||
setterVersion = "v2"
|
||||
l := setters.LookupSetters{}
|
||||
|
||||
// backwards compatibility for resources with setter v1
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{&kio.LocalPackageReader{PackagePath: args[0]}},
|
||||
Filters: []kio.Filter{&l},
|
||||
}.Execute()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(l.SetterCounts) > 0 {
|
||||
setterVersion = "v1"
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SetRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
@@ -55,15 +83,37 @@ func (r *SetRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
r.Perform.Value = args[2]
|
||||
}
|
||||
|
||||
if setterVersion == "" {
|
||||
if len(args) < 3 {
|
||||
setterVersion = "v1"
|
||||
} else if err := initSetterVersion(c, args); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if setterVersion == "v2" {
|
||||
var err error
|
||||
r.Set.Name = args[1]
|
||||
r.Set.Value = args[2]
|
||||
r.Set.Description = r.Perform.Description
|
||||
r.Set.SetBy = r.Perform.SetBy
|
||||
r.OpenAPIFile, err = ext.GetOpenAPIFile(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SetRunner) runE(c *cobra.Command, args []string) error {
|
||||
|
||||
if setterVersion == "v2" {
|
||||
count, err := r.Set.Set(r.OpenAPIFile, args[0])
|
||||
fmt.Fprintf(c.OutOrStdout(), "set %d fields\n", count)
|
||||
return handleError(c, err)
|
||||
}
|
||||
if len(args) == 3 {
|
||||
return handleError(c, r.perform(c, args))
|
||||
}
|
||||
|
||||
return handleError(c, lookup(r.Lookup, c, args))
|
||||
}
|
||||
|
||||
|
||||
278
cmd/config/internal/commands/cmdset_test.go
Normal file
278
cmd/config/internal/commands/cmdset_test.go
Normal file
@@ -0,0 +1,278 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
)
|
||||
|
||||
func TestSetCommand(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
inputOpenAPI string
|
||||
input string
|
||||
args []string
|
||||
out string
|
||||
expectedOpenAPI string
|
||||
expectedResources string
|
||||
}{
|
||||
{
|
||||
name: "set replicas",
|
||||
args: []string{"replicas", "4", "--description", "hi there", "--set-by", "pw"},
|
||||
out: "set 1 fields\n",
|
||||
inputOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
description: hello world
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
setBy: me
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`,
|
||||
expectedOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
description: hi there
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "4"
|
||||
setBy: pw
|
||||
`,
|
||||
expectedResources: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 4 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "set replicas no description",
|
||||
args: []string{"replicas", "4"},
|
||||
out: "set 1 fields\n",
|
||||
inputOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
description: hello world
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
setBy: me
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`,
|
||||
expectedOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
description: hello world
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "4"
|
||||
setBy: me
|
||||
`,
|
||||
expectedResources: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 4 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "set image",
|
||||
args: []string{"tag", "1.8.1"},
|
||||
out: "set 1 fields\n",
|
||||
inputOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.image:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image
|
||||
value: "nginx"
|
||||
io.k8s.cli.setters.tag:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: tag
|
||||
value: "1.7.9"
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: IMAGE:TAG
|
||||
values:
|
||||
- marker: IMAGE
|
||||
ref: '#/definitions/io.k8s.cli.setters.image'
|
||||
- marker: TAG
|
||||
ref: '#/definitions/io.k8s.cli.setters.tag'
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref":"#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: sidecar
|
||||
image: sidecar:1.7.9
|
||||
`,
|
||||
expectedOpenAPI: `
|
||||
apiVersion: v1alpha1
|
||||
kind: Example
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.image:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image
|
||||
value: "nginx"
|
||||
io.k8s.cli.setters.tag:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: tag
|
||||
value: "1.8.1"
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: IMAGE:TAG
|
||||
values:
|
||||
- marker: IMAGE
|
||||
ref: '#/definitions/io.k8s.cli.setters.image'
|
||||
- marker: TAG
|
||||
ref: '#/definitions/io.k8s.cli.setters.tag'
|
||||
|
||||
`,
|
||||
expectedResources: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.8.1 # {"$ref":"#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: sidecar
|
||||
image: sidecar:1.7.9
|
||||
`,
|
||||
},
|
||||
}
|
||||
for i := range tests {
|
||||
test := tests[i]
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
// reset the openAPI afterward
|
||||
openapi.ResetOpenAPI()
|
||||
defer openapi.ResetOpenAPI()
|
||||
|
||||
f, err := ioutil.TempFile("", "k8s-cli-")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
err = ioutil.WriteFile(f.Name(), []byte(test.inputOpenAPI), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
old := ext.GetOpenAPIFile
|
||||
defer func() { ext.GetOpenAPIFile = old }()
|
||||
ext.GetOpenAPIFile = func(args []string) (s string, err error) {
|
||||
return f.Name(), nil
|
||||
}
|
||||
|
||||
r, err := ioutil.TempFile("", "k8s-cli-*.yaml")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
defer os.Remove(r.Name())
|
||||
err = ioutil.WriteFile(r.Name(), []byte(test.input), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
runner := commands.NewSetRunner("")
|
||||
out := &bytes.Buffer{}
|
||||
runner.Command.SetOut(out)
|
||||
runner.Command.SetArgs(append([]string{r.Name()}, test.args...))
|
||||
err = runner.Command.Execute()
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if !assert.Equal(t, test.out, out.String()) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
actualResources, err := ioutil.ReadFile(r.Name())
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(test.expectedResources),
|
||||
strings.TrimSpace(string(actualResources))) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
actualOpenAPI, err := ioutil.ReadFile(f.Name())
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(test.expectedOpenAPI),
|
||||
strings.TrimSpace(string(actualOpenAPI))) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -143,6 +143,8 @@ func TestCmd_failFiles(t *testing.T) {
|
||||
// fmt the files
|
||||
r := commands.GetFmtRunner("")
|
||||
r.Command.SetArgs([]string{"notrealfile"})
|
||||
r.Command.SilenceUsage = true
|
||||
r.Command.SilenceErrors = true
|
||||
err := r.Command.Execute()
|
||||
assert.EqualError(t, err, "lstat notrealfile: no such file or directory")
|
||||
}
|
||||
|
||||
@@ -40,6 +40,10 @@ func GetRunFnRunner(name string) *RunFnRunner {
|
||||
r.Command.Flags().StringVar(
|
||||
&r.Image, "image", "",
|
||||
"run this image as a function instead of discovering them.")
|
||||
r.Command.Flags().BoolVar(
|
||||
&r.Network, "network", false, "enable network access for functions that declare it")
|
||||
r.Command.Flags().StringVar(
|
||||
&r.NetworkName, "network-name", "bridge", "the docker network to run the container in")
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -56,6 +60,8 @@ type RunFnRunner struct {
|
||||
FnPaths []string
|
||||
Image string
|
||||
RunFns runfn.RunFns
|
||||
Network bool
|
||||
NetworkName string
|
||||
}
|
||||
|
||||
func (r *RunFnRunner) runE(c *cobra.Command, args []string) error {
|
||||
@@ -188,6 +194,8 @@ func (r *RunFnRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
Output: output,
|
||||
Input: input,
|
||||
Path: path,
|
||||
Network: r.Network,
|
||||
NetworkName: r.NetworkName,
|
||||
}
|
||||
|
||||
// don't consider args for the function
|
||||
|
||||
@@ -25,6 +25,8 @@ func TestRunFnCommand_preRunE(t *testing.T) {
|
||||
input io.Reader
|
||||
output io.Writer
|
||||
functionPaths []string
|
||||
network bool
|
||||
networkName string
|
||||
}{
|
||||
{
|
||||
name: "config map",
|
||||
@@ -86,6 +88,40 @@ metadata:
|
||||
data: {}
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "network enabled",
|
||||
args: []string{"run", "dir", "--image", "foo:bar", "--network"},
|
||||
path: "dir",
|
||||
network: true,
|
||||
networkName: "bridge",
|
||||
expected: `
|
||||
metadata:
|
||||
name: function-input
|
||||
annotations:
|
||||
config.kubernetes.io/function: |
|
||||
container: {image: 'foo:bar'}
|
||||
data: {}
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "with network name",
|
||||
args: []string{"run", "dir", "--image", "foo:bar", "--network", "--network-name", "foo"},
|
||||
path: "dir",
|
||||
network: true,
|
||||
networkName: "foo",
|
||||
expected: `
|
||||
metadata:
|
||||
name: function-input
|
||||
annotations:
|
||||
config.kubernetes.io/function: |
|
||||
container: {image: 'foo:bar'}
|
||||
data: {}
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -206,6 +242,20 @@ apiVersion: v1
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// check if Network was set
|
||||
if tt.network {
|
||||
if !assert.Equal(t, tt.network, r.RunFns.Network) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Equal(t, tt.networkName, r.RunFns.NetworkName) {
|
||||
t.FailNow()
|
||||
}
|
||||
} else {
|
||||
if !assert.Equal(t, false, r.RunFns.Network) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
// check if FunctionPaths were set
|
||||
if tt.functionPaths == nil {
|
||||
// make Equal work against flag default
|
||||
|
||||
@@ -206,7 +206,7 @@ _file_, or a path (or URL) refering to another
|
||||
kustomization _directory_, e.g.
|
||||
|
||||
```
|
||||
resource:
|
||||
resources:
|
||||
- myNamespace.yaml
|
||||
- sub-dir/some-deployment.yaml
|
||||
- ../../commonbase
|
||||
|
||||
@@ -55,19 +55,36 @@ func (fm *FieldMeta) Read(n *yaml.RNode) error {
|
||||
}
|
||||
b, err := json.Marshal(fe)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
return json.Unmarshal(b, &fm.Extensions)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isExtensionEmpty(x XKustomize) bool {
|
||||
if x.FieldSetter != nil {
|
||||
return false
|
||||
}
|
||||
if x.SetBy != "" {
|
||||
return false
|
||||
}
|
||||
if len(x.PartialFieldSetters) > 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Write writes the FieldMeta to a node
|
||||
func (fm *FieldMeta) Write(n *yaml.RNode) error {
|
||||
fm.Schema.VendorExtensible.AddExtension("x-kustomize", fm.Extensions)
|
||||
if !isExtensionEmpty(fm.Extensions) {
|
||||
fm.Schema.VendorExtensible.AddExtension("x-kustomize", fm.Extensions)
|
||||
} else {
|
||||
delete(fm.Schema.VendorExtensible.Extensions, "x-kustomize")
|
||||
}
|
||||
b, err := json.Marshal(fm.Schema)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
n.YNode().LineComment = string(b)
|
||||
return nil
|
||||
|
||||
@@ -408,6 +408,17 @@ const (
|
||||
|
||||
var functionAnnotationKeys = []string{FunctionAnnotationKey, oldFunctionAnnotationKey}
|
||||
|
||||
// GetFunction parses the config function from the object if it is found
|
||||
func GetFunction(n *yaml.RNode, meta yaml.ResourceMeta) (*yaml.RNode, error) {
|
||||
for _, s := range functionAnnotationKeys {
|
||||
fn := meta.Annotations[s]
|
||||
if fn != "" {
|
||||
return yaml.Parse(fn)
|
||||
}
|
||||
}
|
||||
return n.Pipe(yaml.Lookup("metadata", "configFn"))
|
||||
}
|
||||
|
||||
// GetContainerName returns the container image for an API if one exists
|
||||
func GetContainerName(n *yaml.RNode) (string, string) {
|
||||
meta, _ := n.GetMeta()
|
||||
@@ -415,14 +426,10 @@ func GetContainerName(n *yaml.RNode) (string, string) {
|
||||
// path to the function, this will be mounted into the container
|
||||
path := meta.Annotations[kioutil.PathAnnotation]
|
||||
|
||||
// check previous keys for backwards compatibility
|
||||
for _, s := range functionAnnotationKeys {
|
||||
functionAnnotation := meta.Annotations[s]
|
||||
if functionAnnotation != "" {
|
||||
annotationContent, _ := yaml.Parse(functionAnnotation)
|
||||
image, _ := annotationContent.Pipe(yaml.Lookup("container", "image"))
|
||||
return image.YNode().Value, path
|
||||
}
|
||||
fn, _ := GetFunction(n, meta)
|
||||
if fn != nil {
|
||||
image, _ := fn.Pipe(yaml.Lookup("container", "image"))
|
||||
return yaml.GetValue(image), path
|
||||
}
|
||||
|
||||
container := meta.Annotations["config.kubernetes.io/container"]
|
||||
@@ -434,5 +441,19 @@ func GetContainerName(n *yaml.RNode) (string, string) {
|
||||
if err != nil || yaml.IsMissingOrNull(image) {
|
||||
return "", path
|
||||
}
|
||||
return image.YNode().Value, path
|
||||
return yaml.GetValue(image), path
|
||||
}
|
||||
|
||||
// GetContainerNetworkRequired returns whether or not networking is required for the container
|
||||
func GetContainerNetworkRequired(n *yaml.RNode) (bool, error) {
|
||||
meta, err := n.GetMeta()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
f, err := GetFunction(n, meta)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
networkRequired, _ := f.Pipe(yaml.Lookup("container", "network", "required"))
|
||||
return yaml.GetValue(networkRequired) == "true", nil
|
||||
}
|
||||
|
||||
@@ -309,6 +309,79 @@ metadata:
|
||||
`, b.String())
|
||||
}
|
||||
|
||||
func Test_GetFunction(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
resource string
|
||||
expectedFn string
|
||||
missingFn bool
|
||||
}{
|
||||
|
||||
// fn annotation
|
||||
{
|
||||
name: "fn annotation",
|
||||
resource: `
|
||||
apiVersion: v1beta1
|
||||
kind: Example
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/function: |-
|
||||
container: foo:v1.0.0
|
||||
`,
|
||||
expectedFn: `container: foo:v1.0.0`,
|
||||
},
|
||||
|
||||
// legacy fn style
|
||||
{name: "legacy fn meta",
|
||||
resource: `
|
||||
apiVersion: v1beta1
|
||||
kind: Example
|
||||
metadata:
|
||||
configFn:
|
||||
container: foo:v1.0.0
|
||||
`,
|
||||
expectedFn: `container: foo:v1.0.0`,
|
||||
},
|
||||
|
||||
// no fn
|
||||
{name: "no fn",
|
||||
resource: `
|
||||
apiVersion: v1beta1
|
||||
kind: Example
|
||||
metadata:
|
||||
annotations: {}
|
||||
`,
|
||||
missingFn: true,
|
||||
},
|
||||
|
||||
// test network, etc...
|
||||
}
|
||||
|
||||
for i := range tests {
|
||||
tt := tests[i]
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
resource := yaml.MustParse(tt.resource)
|
||||
meta, err := resource.GetMeta()
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
fn, err := GetFunction(resource, meta)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if tt.missingFn {
|
||||
if !assert.Nil(t, fn) {
|
||||
t.FailNow()
|
||||
}
|
||||
} else {
|
||||
if !assert.Equal(t, strings.TrimSpace(fn.MustString()), strings.TrimSpace(tt.expectedFn)) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_GetContainerName(t *testing.T) {
|
||||
// make sure gcr.io works
|
||||
n, err := yaml.Parse(`apiVersion: v1beta1
|
||||
@@ -364,6 +437,76 @@ metadata:
|
||||
assert.Equal(t, "", c)
|
||||
}
|
||||
|
||||
func Test_GetContainerNetworkRequired(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
required bool
|
||||
}{
|
||||
{
|
||||
input: `apiVersion: v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: foo
|
||||
configFn:
|
||||
container:
|
||||
image: gcr.io/kustomize-functions/example-tshirt:v0.1.0
|
||||
network:
|
||||
required: true
|
||||
`,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
input: `apiVersion: v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: foo
|
||||
configFn:
|
||||
container:
|
||||
image: gcr.io/kustomize-functions/example-tshirt:v0.1.0
|
||||
network:
|
||||
required: false
|
||||
`,
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
|
||||
input: `apiVersion: v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: foo
|
||||
configFn:
|
||||
container:
|
||||
image: gcr.io/kustomize-functions/example-tshirt:v0.1.0
|
||||
`,
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
input: `apiVersion: v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
config.kubernetes.io/function: |
|
||||
container:
|
||||
image: gcr.io/kustomize-functions/example-tshirt:v0.1.0
|
||||
network:
|
||||
required: true
|
||||
`,
|
||||
required: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
cfg, err := yaml.Parse(tc.input)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
required, err := GetContainerNetworkRequired(cfg)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tc.required, required)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilter_Filter_defaultNaming(t *testing.T) {
|
||||
cfg, err := yaml.Parse(`apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
|
||||
@@ -6,6 +6,7 @@ package openapi
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"sync"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
@@ -44,11 +45,76 @@ func SchemaForResourceType(t yaml.TypeMeta) *ResourceSchema {
|
||||
return &ResourceSchema{Schema: rs}
|
||||
}
|
||||
|
||||
// SupplementaryOpenAPIFieldName is the conventional field name (JSON/YAML) containing
|
||||
// supplementary OpenAPI definitions.
|
||||
const SupplementaryOpenAPIFieldName = "openAPI"
|
||||
|
||||
// AddSchemaFromFile reads the file at path and parses the OpenAPI definitions
|
||||
// from the field "openAPI"
|
||||
func AddSchemaFromFile(path string) error {
|
||||
return AddSchemaFromFileUsingField(path, SupplementaryOpenAPIFieldName)
|
||||
}
|
||||
|
||||
// AddSchemaFromFileUsingField reads the file at path and parses the OpenAPI definitions
|
||||
// from the specified field. If field is the empty string, use the whole document as
|
||||
// OpenAPI.
|
||||
func AddSchemaFromFileUsingField(path, field string) error {
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// parse the yaml file (json is a subset of yaml, so will also parse)
|
||||
y, err := yaml.Parse(string(b))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if field != "" {
|
||||
// get the field containing the openAPI
|
||||
m := y.Field(field)
|
||||
if yaml.IsFieldEmpty(m) {
|
||||
// doesn't contain openAPI definitions
|
||||
return nil
|
||||
}
|
||||
y = m.Value
|
||||
}
|
||||
|
||||
oAPI, err := y.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// convert the yaml openAPI to a JSON string by unmarshalling it to an
|
||||
// interface{} and the marshalling it to a string
|
||||
var o interface{}
|
||||
err = yaml.Unmarshal([]byte(oAPI), &o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
j, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// add the json schema to the global schema
|
||||
_, err = AddSchema(j)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddSchema parses s, and adds definitions from s to the global schema.
|
||||
func AddSchema(s []byte) (*spec.Schema, error) {
|
||||
return parse(s)
|
||||
}
|
||||
|
||||
// ResetOpenAPI resets the openapi data to empty
|
||||
func ResetOpenAPI() {
|
||||
globalSchema = openapiData{}
|
||||
}
|
||||
|
||||
// AddDefinitions adds the definitions to the global schema.
|
||||
func AddDefinitions(definitions spec.Definitions) {
|
||||
// initialize values if they have not yet been set
|
||||
@@ -123,7 +189,7 @@ func GetSchema(s string) (*ResourceSchema, error) {
|
||||
// schema as part of the global schema.
|
||||
// Must be called before the schema is used.
|
||||
func SuppressBuiltInSchemaUse() {
|
||||
globalSchema.noUseBuiltInSchema = false
|
||||
globalSchema.noUseBuiltInSchema = true
|
||||
}
|
||||
|
||||
// Elements returns the Schema for the elements of an array.
|
||||
|
||||
@@ -5,6 +5,7 @@ package openapi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -40,7 +41,7 @@ func TestNoUseBuiltInSchema_AddSchema(t *testing.T) {
|
||||
t.FailNow()
|
||||
}
|
||||
s, err := GetSchema(`{"$ref": "#/definitions/io.k8s.config.setters.replicas"}`)
|
||||
if !assert.Greater(t, len(globalSchema.schema.Definitions), 1) {
|
||||
if !assert.Equal(t, len(globalSchema.schema.Definitions), 1) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.NoError(t, err) {
|
||||
@@ -122,3 +123,117 @@ func TestSchemaForResourceType(t *testing.T) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddSchemaFromFile(t *testing.T) {
|
||||
ResetOpenAPI()
|
||||
inputyaml := `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.image-name:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image-name
|
||||
value: "nginx"
|
||||
`
|
||||
f, err := ioutil.TempFile("", "openapi-")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.NoError(t, ioutil.WriteFile(f.Name(), []byte(inputyaml), 0600)) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
err = AddSchemaFromFile(f.Name())
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
s, err := GetSchema(`{"$ref": "#/definitions/io.k8s.cli.setters.image-name"}`)
|
||||
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Greater(t, len(globalSchema.schema.Definitions), 200) {
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal(t, `map[x-k8s-cli:map[setter:map[name:image-name value:nginx]]]`,
|
||||
fmt.Sprintf("%v", s.Schema.Extensions))
|
||||
}
|
||||
|
||||
func TestPopulateDefsInOpenAPI_Substitution(t *testing.T) {
|
||||
ResetOpenAPI()
|
||||
inputyaml := `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.image-name:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image-name
|
||||
value: "nginx"
|
||||
io.k8s.cli.setters.image-tag:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image-tag
|
||||
value: "1.8.1"
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: IMAGE_NAME:IMAGE_TAG
|
||||
values:
|
||||
- marker: "IMAGE_NAME"
|
||||
ref: "#/definitions/io.k8s.cli.setters.image-name"
|
||||
- marker: "IMAGE_TAG"
|
||||
ref: "#/definitions/io.k8s.cli.setters.image-tag"
|
||||
`
|
||||
|
||||
f, err := ioutil.TempFile("", "openapi-")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.NoError(t, ioutil.WriteFile(f.Name(), []byte(inputyaml), 0600)) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if !assert.NoError(t, AddSchemaFromFile(f.Name())) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
s, err := GetSchema(`{"$ref": "#/definitions/io.k8s.cli.substitutions.image"}`)
|
||||
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Greater(t, len(globalSchema.schema.Definitions), 200) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
assert.Equal(t,
|
||||
`map[x-k8s-cli:map[substitution:map[name:image pattern:IMAGE_NAME:IMAGE_TAG`+
|
||||
` values:[map[marker:IMAGE_NAME ref:#/definitions/io.k8s.cli.setters.image-name]`+
|
||||
` map[marker:IMAGE_TAG ref:#/definitions/io.k8s.cli.setters.image-tag]]]]]`,
|
||||
fmt.Sprintf("%v", s.Schema.Extensions))
|
||||
}
|
||||
|
||||
func TestAddSchemaFromFile_empty(t *testing.T) {
|
||||
ResetOpenAPI()
|
||||
inputyaml := `
|
||||
kind: Example
|
||||
`
|
||||
|
||||
f, err := ioutil.TempFile("", "openapi-")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.NoError(t, ioutil.WriteFile(f.Name(), []byte(inputyaml), 0600)) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if !assert.NoError(t, AddSchemaFromFile(f.Name())) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if !assert.Equal(t, len(globalSchema.schema.Definitions), 0) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,12 @@ type RunFns struct {
|
||||
// Input can be set to read the Resources from Input rather than from a directory
|
||||
Input io.Reader
|
||||
|
||||
// Network enables network access for functions that declare it
|
||||
Network bool
|
||||
|
||||
// NetworkName is the name of the docker network to use for the container
|
||||
NetworkName string
|
||||
|
||||
// Output can be set to write the result to Output rather than back to the directory
|
||||
Output io.Writer
|
||||
|
||||
@@ -52,7 +58,7 @@ type RunFns struct {
|
||||
NoFunctionsFromInput *bool
|
||||
|
||||
// for testing purposes only
|
||||
containerFilterProvider func(string, string, *yaml.RNode) kio.Filter
|
||||
containerFilterProvider func(string, string, string, *yaml.RNode) kio.Filter
|
||||
}
|
||||
|
||||
// Execute runs the command
|
||||
@@ -119,7 +125,10 @@ func (r RunFns) getFilters(nodes []*yaml.RNode) ([]kio.Filter, error) {
|
||||
fltrs = append(fltrs, f...)
|
||||
|
||||
// explicit filters from a list of directories
|
||||
f = r.getFunctionsFromFunctions()
|
||||
f, err = r.getFunctionsFromFunctions()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fltrs = append(fltrs, f...)
|
||||
|
||||
return fltrs, nil
|
||||
@@ -160,8 +169,22 @@ func (r RunFns) getFunctionsFromInput(nodes []*yaml.RNode) ([]kio.Filter, error)
|
||||
sortFns(buff)
|
||||
for i := range buff.Nodes {
|
||||
api := buff.Nodes[i]
|
||||
network := ""
|
||||
img, path := filters.GetContainerName(api)
|
||||
fltrs = append(fltrs, r.containerFilterProvider(img, path, api))
|
||||
|
||||
required, err := filters.GetContainerNetworkRequired(api)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if required {
|
||||
if !r.Network {
|
||||
// TODO(eddizane): Provide error info about which function needs the network
|
||||
return fltrs, errors.Errorf("network required but not enabled with --network")
|
||||
}
|
||||
network = r.NetworkName
|
||||
}
|
||||
|
||||
fltrs = append(fltrs, r.containerFilterProvider(img, path, network, api))
|
||||
}
|
||||
return fltrs, nil
|
||||
}
|
||||
@@ -182,8 +205,22 @@ func (r RunFns) getFunctionsFromFunctionPaths() ([]kio.Filter, error) {
|
||||
}
|
||||
for i := range buff.Nodes {
|
||||
api := buff.Nodes[i]
|
||||
network := ""
|
||||
img, path := filters.GetContainerName(api)
|
||||
c := r.containerFilterProvider(img, path, api)
|
||||
|
||||
required, err := filters.GetContainerNetworkRequired(api)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if required {
|
||||
if !r.Network {
|
||||
// TODO(eddiezane): Provide error info about which function needs the network
|
||||
return fltrs, errors.Errorf("network required but not enabled with --network")
|
||||
}
|
||||
network = r.NetworkName
|
||||
}
|
||||
|
||||
c := r.containerFilterProvider(img, path, network, api)
|
||||
cf, ok := c.(*filters.ContainerFilter)
|
||||
if ok {
|
||||
// functions provided by FunctionPaths are globally scoped
|
||||
@@ -196,12 +233,26 @@ func (r RunFns) getFunctionsFromFunctionPaths() ([]kio.Filter, error) {
|
||||
|
||||
// getFunctionsFromFunctions returns the set of explicitly provided functions as
|
||||
// Filters
|
||||
func (r RunFns) getFunctionsFromFunctions() []kio.Filter {
|
||||
func (r RunFns) getFunctionsFromFunctions() ([]kio.Filter, error) {
|
||||
var fltrs []kio.Filter
|
||||
for i := range r.Functions {
|
||||
api := r.Functions[i]
|
||||
network := ""
|
||||
img, path := filters.GetContainerName(api)
|
||||
c := r.containerFilterProvider(img, path, api)
|
||||
|
||||
required, err := filters.GetContainerNetworkRequired(api)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if required {
|
||||
if !r.Network {
|
||||
// TODO(eddizane): Provide error info about which function needs the network
|
||||
return fltrs, errors.Errorf("network required but not enabled with --network")
|
||||
}
|
||||
network = r.NetworkName
|
||||
}
|
||||
|
||||
c := r.containerFilterProvider(img, path, network, api)
|
||||
cf, ok := c.(*filters.ContainerFilter)
|
||||
if ok {
|
||||
// functions provided by Functions are globally scoped
|
||||
@@ -209,7 +260,7 @@ func (r RunFns) getFunctionsFromFunctions() []kio.Filter {
|
||||
}
|
||||
fltrs = append(fltrs, c)
|
||||
}
|
||||
return fltrs
|
||||
return fltrs, nil
|
||||
}
|
||||
|
||||
// sortFns sorts functions so that functions with the longest paths come first
|
||||
@@ -278,10 +329,11 @@ func (r *RunFns) init() {
|
||||
|
||||
// if containerFilterProvider hasn't been set, use the default
|
||||
if r.containerFilterProvider == nil {
|
||||
r.containerFilterProvider = func(image, path string, api *yaml.RNode) kio.Filter {
|
||||
r.containerFilterProvider = func(image, path, network string, api *yaml.RNode) kio.Filter {
|
||||
cf := &filters.ContainerFilter{
|
||||
Image: image,
|
||||
Config: api,
|
||||
Network: network,
|
||||
StorageMounts: r.StorageMounts,
|
||||
GlobalScope: r.GlobalScope,
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ kind:
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
filter := instance.containerFilterProvider("example.com:version", "", api)
|
||||
filter := instance.containerFilterProvider("example.com:version", "", "", api)
|
||||
assert.Equal(t, &filters.ContainerFilter{Image: "example.com:version", Config: api}, filter)
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ kind:
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
filter := instance.containerFilterProvider("example.com:version", "", api)
|
||||
filter := instance.containerFilterProvider("example.com:version", "", "", api)
|
||||
assert.Equal(t, &filters.ContainerFilter{
|
||||
Image: "example.com:version", Config: api, GlobalScope: true}, filter)
|
||||
}
|
||||
@@ -659,8 +659,8 @@ func setupTest(t *testing.T) string {
|
||||
// getFilterProvider fakes the creation of a filter, replacing the ContainerFiler with
|
||||
// a filter to s/kind: Deployment/kind: StatefulSet/g.
|
||||
// this can be used to simulate running a filter.
|
||||
func getFilterProvider(t *testing.T) func(string, string, *yaml.RNode) kio.Filter {
|
||||
return func(s, _ string, node *yaml.RNode) kio.Filter {
|
||||
func getFilterProvider(t *testing.T) func(string, string, string, *yaml.RNode) kio.Filter {
|
||||
return func(s, _, _ string, node *yaml.RNode) kio.Filter {
|
||||
// parse the filter from the input
|
||||
filter := yaml.YFilter{}
|
||||
b := &bytes.Buffer{}
|
||||
|
||||
200
kyaml/setters2/add.go
Normal file
200
kyaml/setters2/add.go
Normal file
@@ -0,0 +1,200 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package setters2
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// Add creates or updates setter or substitution references from resource fields.
|
||||
// Requires that at least one of FieldValue and FieldName have been set.
|
||||
type Add struct {
|
||||
// FieldValue if set will add the OpenAPI reference to fields if they have this value.
|
||||
// Optional. If unspecified match all field values.
|
||||
FieldValue string
|
||||
|
||||
// FieldName if set will add the OpenAPI reference to fields with this name or path
|
||||
// FieldName may be the full name of the field, full path to the field, or the path suffix.
|
||||
// e.g. all of the following would match spec.template.spec.containers.image --
|
||||
// [image, containers.image, spec.containers.image, template.spec.containers.image,
|
||||
// spec.template.spec.containers.image]
|
||||
// Optional. If unspecified match all field names.
|
||||
FieldName string
|
||||
|
||||
// Ref is the OpenAPI reference to set on the matching fields as a comment.
|
||||
Ref string
|
||||
}
|
||||
|
||||
// Filter implements yaml.Filter
|
||||
func (a *Add) Filter(object *yaml.RNode) (*yaml.RNode, error) {
|
||||
if a.FieldName == "" && a.FieldValue == "" {
|
||||
return nil, errors.Errorf("must specify either fieldName or fieldValue")
|
||||
}
|
||||
if a.Ref == "" {
|
||||
return nil, errors.Errorf("must specify ref")
|
||||
}
|
||||
return object, accept(a, object)
|
||||
}
|
||||
|
||||
// visitScalar implements visitor
|
||||
// visitScalar will set the field metadata on each scalar field whose name + value match
|
||||
func (a *Add) visitScalar(object *yaml.RNode, p string) error {
|
||||
// check if the field matches
|
||||
if a.FieldName != "" && !strings.HasSuffix(p, a.FieldName) {
|
||||
return nil
|
||||
}
|
||||
if a.FieldValue != "" && a.FieldValue != object.YNode().Value {
|
||||
return nil
|
||||
}
|
||||
|
||||
// read the field metadata
|
||||
fm := fieldmeta.FieldMeta{}
|
||||
if err := fm.Read(object); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create the ref on the field metadata
|
||||
r, err := spec.NewRef(a.Ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fm.Schema.Ref = r
|
||||
|
||||
// write the field metadata
|
||||
if err := fm.Write(object); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
// CLIDefinitionsPrefix is the prefix for cli definition keys.
|
||||
CLIDefinitionsPrefix = "io.k8s.cli."
|
||||
|
||||
// SetterDefinitionPrefix is the prefix for setter definition keys.
|
||||
SetterDefinitionPrefix = CLIDefinitionsPrefix + "setters."
|
||||
|
||||
// SubstitutionDefinitionPrefix is the prefix for substitution definition keys.
|
||||
SubstitutionDefinitionPrefix = CLIDefinitionsPrefix + "substitutions."
|
||||
|
||||
// DefinitionsPrefix is the prefix used to reference definitions in the OpenAPI
|
||||
DefinitionsPrefix = "#/definitions/"
|
||||
)
|
||||
|
||||
// SetterDefinition may be used to update a files OpenAPI definitions with a new setter.
|
||||
type SetterDefinition struct {
|
||||
// Name is the name of the setter to create or update.
|
||||
Name string `yaml:"name"`
|
||||
|
||||
// Value is the value of the setter.
|
||||
Value string `yaml:"value"`
|
||||
|
||||
// SetBy is the person or role that last set the value.
|
||||
SetBy string `yaml:"setBy,omitempty"`
|
||||
|
||||
// Description is a description of the value.
|
||||
Description string `yaml:"description,omitempty"`
|
||||
|
||||
// Count is the number of fields set by this setter.
|
||||
Count int `yaml:"count,omitempty"`
|
||||
}
|
||||
|
||||
func (sd SetterDefinition) AddToFile(path string) error {
|
||||
return yaml.UpdateFile(sd, path)
|
||||
}
|
||||
|
||||
func (sd SetterDefinition) Filter(object *yaml.RNode) (*yaml.RNode, error) {
|
||||
key := SetterDefinitionPrefix + sd.Name
|
||||
|
||||
def, err := object.Pipe(yaml.LookupCreate(
|
||||
yaml.MappingNode, openapi.SupplementaryOpenAPIFieldName, "definitions", key))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if sd.Description != "" {
|
||||
err = def.PipeE(yaml.FieldSetter{Name: "description", StringValue: sd.Description})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// don't write the description to the extension
|
||||
sd.Description = ""
|
||||
}
|
||||
|
||||
ext, err := def.Pipe(yaml.LookupCreate(yaml.MappingNode, K8sCliExtensionKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b, err := yaml.Marshal(sd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
y, err := yaml.Parse(string(b))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := ext.PipeE(yaml.SetField("setter", y)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return object, nil
|
||||
}
|
||||
|
||||
// SetterDefinition may be used to update a files OpenAPI definitions with a new substitution.
|
||||
type SubstitutionDefinition struct {
|
||||
// Name is the name of the substitution to create or update
|
||||
Name string `yaml:"name"`
|
||||
|
||||
// Pattern is the substitution pattern into which setter values are substituted
|
||||
Pattern string `yaml:"pattern"`
|
||||
|
||||
// Values are setters which are substituted into pattern to produce a field value
|
||||
Values []Value `yaml:"values"`
|
||||
}
|
||||
|
||||
type Value struct {
|
||||
// Marker is the string marker in pattern that is replace by the referenced setter.
|
||||
Marker string `yaml:"marker"`
|
||||
|
||||
// Ref is a reference to a setter to pull the replacement value from.
|
||||
Ref string `yaml:"ref"`
|
||||
}
|
||||
|
||||
func (sd SubstitutionDefinition) AddToFile(path string) error {
|
||||
return yaml.UpdateFile(sd, path)
|
||||
}
|
||||
|
||||
func (sd SubstitutionDefinition) Filter(object *yaml.RNode) (*yaml.RNode, error) {
|
||||
// create the substitution extension value by marshalling the SubstitutionDefinition itself
|
||||
b, err := yaml.Marshal(sd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sub, err := yaml.Parse(string(b))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// lookup or create the definition for the substitution
|
||||
defKey := SubstitutionDefinitionPrefix + sd.Name
|
||||
def, err := object.Pipe(yaml.LookupCreate(
|
||||
yaml.MappingNode, openapi.SupplementaryOpenAPIFieldName, "definitions", defKey, "x-k8s-cli"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// set the substitution on the definition
|
||||
if err := def.PipeE(yaml.SetField("substitution", sub)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return object, nil
|
||||
}
|
||||
358
kyaml/setters2/add_test.go
Normal file
358
kyaml/setters2/add_test.go
Normal file
@@ -0,0 +1,358 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package setters2
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
func TestAdd_Filter(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
add Add
|
||||
input string
|
||||
expected string
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "add-replicas",
|
||||
add: Add{
|
||||
FieldValue: "3",
|
||||
Ref: "#/definitions/io.k8s.cli.setters.replicas",
|
||||
},
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "add-replicas-annotations",
|
||||
add: Add{
|
||||
FieldValue: "3",
|
||||
Ref: "#/definitions/io.k8s.cli.setters.replicas",
|
||||
},
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
something: 3
|
||||
spec:
|
||||
replicas: 3
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
something: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
|
||||
spec:
|
||||
replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "add-replicas-name",
|
||||
add: Add{
|
||||
FieldValue: "3",
|
||||
FieldName: "replicas",
|
||||
Ref: "#/definitions/io.k8s.cli.setters.replicas",
|
||||
},
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
something: 3
|
||||
spec:
|
||||
replicas: 3
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
something: 3
|
||||
spec:
|
||||
replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "add-replicas-2x",
|
||||
add: Add{
|
||||
FieldValue: "3",
|
||||
FieldName: "replicas",
|
||||
Ref: "#/definitions/io.k8s.cli.setters.replicas",
|
||||
},
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
replicas: 3
|
||||
spec:
|
||||
replicas: 3
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
|
||||
spec:
|
||||
replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "add-replicas-1x",
|
||||
add: Add{
|
||||
FieldValue: "3",
|
||||
FieldName: "spec.replicas",
|
||||
Ref: "#/definitions/io.k8s.cli.setters.replicas",
|
||||
},
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
replicas: 3
|
||||
spec:
|
||||
replicas: 3
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
replicas: 3
|
||||
spec:
|
||||
replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "add-replicas-error",
|
||||
add: Add{
|
||||
Ref: "#/definitions/io.k8s.cli.setters.replicas",
|
||||
},
|
||||
err: "must specify either fieldName or fieldValue",
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
`,
|
||||
},
|
||||
}
|
||||
for i := range tests {
|
||||
test := tests[i]
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
// parse the input to be modified
|
||||
r, err := yaml.Parse(test.input)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// invoke add
|
||||
result, err := test.add.Filter(r)
|
||||
if test.err != "" {
|
||||
if !assert.Equal(t, test.err, err.Error()) {
|
||||
t.FailNow()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// compare the actual and expected output
|
||||
actual, err := result.String()
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
actual = strings.TrimSpace(actual)
|
||||
expected := strings.TrimSpace(test.expected)
|
||||
if !assert.Equal(t, expected, actual) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var resourcefile = `apiVersion: resource.dev/v1alpha1
|
||||
kind: resourcefile
|
||||
metadata:
|
||||
name: hello-world-set
|
||||
upstream:
|
||||
type: git
|
||||
git:
|
||||
commit: 5c1c019b59299a4f6c7edd1ff5ff54d720621bbe
|
||||
directory: /package-examples/helloworld-set
|
||||
ref: v0.1.0
|
||||
packageMetadata:
|
||||
shortDescription: example package using setters`
|
||||
|
||||
func TestAdd_Filter2(t *testing.T) {
|
||||
path := filepath.Join(os.TempDir(), "resourcefile")
|
||||
|
||||
//write initial resourcefile to temp path
|
||||
err := ioutil.WriteFile(path, []byte(resourcefile), 0666)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
//add a setter definition
|
||||
sd := SetterDefinition{
|
||||
Name: "image",
|
||||
Value: "1",
|
||||
}
|
||||
|
||||
err = sd.AddToFile(path)
|
||||
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// update setter definition
|
||||
sd2 := SetterDefinition{
|
||||
Name: "image",
|
||||
Value: "2",
|
||||
}
|
||||
|
||||
err = sd2.AddToFile(path)
|
||||
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
expected := `apiVersion: resource.dev/v1alpha1
|
||||
kind: resourcefile
|
||||
metadata:
|
||||
name: hello-world-set
|
||||
upstream:
|
||||
type: git
|
||||
git:
|
||||
commit: 5c1c019b59299a4f6c7edd1ff5ff54d720621bbe
|
||||
directory: /package-examples/helloworld-set
|
||||
ref: v0.1.0
|
||||
packageMetadata:
|
||||
shortDescription: example package using setters
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.image:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image
|
||||
value: "2"
|
||||
`
|
||||
assert.Equal(t, expected, string(b))
|
||||
}
|
||||
|
||||
func TestAddUpdateSubstitution(t *testing.T) {
|
||||
path := filepath.Join(os.TempDir(), "resourcefile")
|
||||
|
||||
//write initial resourcefile to temp path
|
||||
err := ioutil.WriteFile(path, []byte(resourcefile), 0666)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
value1 := Value{
|
||||
Marker: "IMAGE_NAME",
|
||||
Ref: "#/definitions/io.k8s.cli.setters.image-name",
|
||||
}
|
||||
|
||||
value2 := Value{
|
||||
Marker: "IMAGE_TAG",
|
||||
Ref: "#/definitions/io.k8s.cli.setters.image-tag",
|
||||
}
|
||||
|
||||
values := []Value{value1, value2}
|
||||
|
||||
//add a setter definition
|
||||
subd := SubstitutionDefinition{
|
||||
Name: "image",
|
||||
Pattern: "IMAGE_NAME:IMAGE_TAG",
|
||||
Values: values,
|
||||
}
|
||||
|
||||
err = subd.AddToFile(path)
|
||||
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// update setter definition
|
||||
subd2 := SubstitutionDefinition{
|
||||
Name: "image",
|
||||
Pattern: "IMAGE_NAME:IMAGE_TAG2",
|
||||
}
|
||||
|
||||
err = subd2.AddToFile(path)
|
||||
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
expected := `apiVersion: resource.dev/v1alpha1
|
||||
kind: resourcefile
|
||||
metadata:
|
||||
name: hello-world-set
|
||||
upstream:
|
||||
type: git
|
||||
git:
|
||||
commit: 5c1c019b59299a4f6c7edd1ff5ff54d720621bbe
|
||||
directory: /package-examples/helloworld-set
|
||||
ref: v0.1.0
|
||||
packageMetadata:
|
||||
shortDescription: example package using setters
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: IMAGE_NAME:IMAGE_TAG2
|
||||
values: []
|
||||
`
|
||||
assert.Equal(t, expected, string(b))
|
||||
}
|
||||
163
kyaml/setters2/doc.go
Normal file
163
kyaml/setters2/doc.go
Normal file
@@ -0,0 +1,163 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Package setters2 contains libraries for setting resource field values from OpenAPI setter
|
||||
// extensions.
|
||||
//
|
||||
// Setters
|
||||
//
|
||||
// Setters are used to programmatically set configuration field values -- e.g. through a cli or ui.
|
||||
//
|
||||
// Setters are defined through OpenAPI definitions using the x-k8s-cli extension.
|
||||
// Note: additional OpenAPI definitions may be registered through openapi.AddSchema([]byte)
|
||||
//
|
||||
// Example OpenAPI schema containing a setter:
|
||||
//
|
||||
// {
|
||||
// "definitions": {
|
||||
// "io.k8s.cli.setters.replicas": {
|
||||
// "x-k8s-cli": {
|
||||
// "setter": {
|
||||
// "name": "replicas",
|
||||
// "value": "4"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Setter fields:
|
||||
//
|
||||
// x-k8s-cli.setter.name: name of the setter
|
||||
// x-k8s-cli.setter.value: value of the setter that should be applied to fields
|
||||
//
|
||||
// The setter definition key must be of the form "io.k8s.cli.setters.NAME", where NAME matches the
|
||||
// value of "x-k8s-cli.setter.name".
|
||||
//
|
||||
// When Set.Filter is called, the named setter will have its value applied to all resource
|
||||
// fields referencing it.
|
||||
//
|
||||
// Fields may reference setters through a yaml comment containing the serialized JSON OpenAPI.
|
||||
//
|
||||
// Example Deployment resource with a "spec.replicas" field set by the "replicas" setter:
|
||||
//
|
||||
// apiVersion: apps/v1
|
||||
// kind: Deployment
|
||||
// metadata:
|
||||
// name: nginx-deployment
|
||||
// spec:
|
||||
// replicas: 4 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
//
|
||||
// If the OpenAPI io.k8s.cli.setters.replicas x-k8s-cli.setter.value was changed from "4" to "5",
|
||||
// then calling Set{Name: "replicas"}.Filter(deployment) would update the Deployment spec.replicas
|
||||
// value from 4 to 5.
|
||||
//
|
||||
// Updated OpenAPI:
|
||||
//
|
||||
// {
|
||||
// "definitions": {
|
||||
// "io.k8s.cli.setters.replicas": {
|
||||
// "x-k8s-cli": {
|
||||
// "setter": {
|
||||
// "name": "replicas",
|
||||
// "value": "5"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Updated Deployment Configuration:
|
||||
//
|
||||
// apiVersion: apps/v1
|
||||
// kind: Deployment
|
||||
// metadata:
|
||||
// name: nginx-deployment
|
||||
// spec:
|
||||
// replicas: 5 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
//
|
||||
// Substitutions
|
||||
//
|
||||
// Substitutions are used to programmatically set configuration field values using multiple
|
||||
// setters which are substituted into a pattern string.
|
||||
//
|
||||
// Substitutions may be used when a field value does not cleanly map to a single setter, but
|
||||
// instead matches some string pattern where setters may be substituted in.
|
||||
//
|
||||
// Fields may reference substitutions the same way they do setters, however substitutions
|
||||
// reference setters from which they are derived.
|
||||
//
|
||||
// Example OpenAPI schema containing a substitution derived from 2 setters:
|
||||
//
|
||||
// {
|
||||
// "definitions": {
|
||||
// "io.k8s.cli.setters.image-name": {
|
||||
// "x-k8s-cli": {
|
||||
// "setter": {
|
||||
// "name": "image-name",
|
||||
// "value": "nginx"
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// "io.k8s.cli.setters.image-tag": {
|
||||
// "x-k8s-cli": {
|
||||
// "setter": {
|
||||
// "name": "image-tag",
|
||||
// "value": "1.8.1"
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// "io.k8s.cli.substitutions.image-name-tag": {
|
||||
// "x-k8s-cli": {
|
||||
// "substitution": {
|
||||
// "name": "image-name-tag",
|
||||
// "pattern": "IMAGE_NAME:IMAGE_TAG",
|
||||
// "values": [
|
||||
// {"marker": "IMAGE_NAME", "ref": "#/definitions/io.k8s.cli.setters.image-name"}
|
||||
// {"marker": "IMAGE_TAG", "ref": "#/definitions/io.k8s.cli.setters.image-tag"}
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Substitution Fields.
|
||||
//
|
||||
// x-k8s-cli.substitution.name: name of the substitution
|
||||
// x-k8s-cli.substitution.pattern: string pattern to substitute markers into
|
||||
// x-k8s-cli.substitution.values.marker: the marker substring within pattern to replace
|
||||
// x-k8s-cli.substitution.values.ref: the setter ref containing the value to replace the marker with
|
||||
//
|
||||
// The substitution is composed of a "pattern" containing markers, and a list of setter "values"
|
||||
// which are substituted into the markers.
|
||||
//
|
||||
// Example Deployment with substitution:
|
||||
//
|
||||
// apiVersion: apps/v1
|
||||
// kind: Deployment
|
||||
// metadata:
|
||||
// name: nginx-deployment
|
||||
// spec:
|
||||
// template:
|
||||
// spec:
|
||||
// containers:
|
||||
// - name: nginx
|
||||
// image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image-name-tag"}
|
||||
//
|
||||
// spec.template.spec.containers[name=nginx].image is set by the "image" substitution any time
|
||||
// either "image-name" or "image-tag" is set. Whenever any setter referenced by a substitution
|
||||
// is set, the substitution will be recalculated by substituting its values into its pattern.
|
||||
//
|
||||
//
|
||||
// If the OpenAPI io.k8s.cli.setters.image-name x-k8s-cli.setter.value was changed from "1.8.1"
|
||||
// to "1.8.2", then calling either Set{Name: "image-name"}.Filter(deployment) or
|
||||
// Set{Name: "image-tag"}.Filter(deployment) would update the Deployment field
|
||||
// spec.template.spec.container[name=nginx].image from "nginx:1.8.1" to "nginx:1.8.2".
|
||||
//
|
||||
// Adding Field References
|
||||
//
|
||||
// References to setters and substitutions may be added to fields using the Add Filter.
|
||||
// Add will write a JSON OpenAPI string as a comment to any fields matching the specified
|
||||
// FieldName add FieldValue.
|
||||
package setters2
|
||||
202
kyaml/setters2/example_test.go
Normal file
202
kyaml/setters2/example_test.go
Normal file
@@ -0,0 +1,202 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package setters2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// ExampleSet demonstrates using Set to replace the current field value in an object
|
||||
func ExampleSet() {
|
||||
openapi.ResetOpenAPI()
|
||||
|
||||
// OpenAPI definitions with setter extensions on definitions
|
||||
schema := `
|
||||
{
|
||||
"definitions": {
|
||||
"io.k8s.cli.setters.replicas": {
|
||||
"x-k8s-cli": {
|
||||
"setter": {
|
||||
"name": "replicas",
|
||||
"value": "4"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
// Resource with field referencing OpenAPI definition
|
||||
deployment := `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`
|
||||
_, err := openapi.AddSchema([]byte(schema)) // add the schema definitions
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
object := yaml.MustParse(deployment) // parse the configuration
|
||||
err = object.PipeE(&Set{Name: "replicas"}) // set replicas from the setter
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println(object.MustString())
|
||||
|
||||
// Output:
|
||||
// apiVersion: apps/v1
|
||||
// kind: Deployment
|
||||
// metadata:
|
||||
// name: nginx-deployment
|
||||
// spec:
|
||||
// replicas: 4 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
}
|
||||
|
||||
// ExampleSet_Substitution demonstrates using Set to substitute a value into the field of
|
||||
// an object. Only part of the field value is modified.
|
||||
func ExampleSet_substitution() {
|
||||
openapi.ResetOpenAPI()
|
||||
|
||||
// set the version setter
|
||||
schema := `
|
||||
{
|
||||
"definitions": {
|
||||
"io.k8s.cli.setters.version": {
|
||||
"x-k8s-cli": {
|
||||
"setter": {
|
||||
"name": "version",
|
||||
"value": "1.8.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io.k8s.cli.substitutions.image": {
|
||||
"x-k8s-cli": {
|
||||
"substitution": {
|
||||
"name": "image",
|
||||
"pattern": "nginx:VERSION",
|
||||
"values": [
|
||||
{"marker": "VERSION", "ref": "#/definitions/io.k8s.cli.setters.version"}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
deployment := `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
`
|
||||
|
||||
_, err := openapi.AddSchema([]byte(schema)) // add the schema definitions
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
object := yaml.MustParse(deployment) // parse the configuration
|
||||
err = object.PipeE(&Set{Name: "version"}) // set replicas from the setter
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Print the object with the update value
|
||||
fmt.Println(object.MustString())
|
||||
|
||||
// Output:
|
||||
// apiVersion: apps/v1
|
||||
// kind: Deployment
|
||||
// metadata:
|
||||
// name: nginx-deployment
|
||||
// spec:
|
||||
// template:
|
||||
// spec:
|
||||
// containers:
|
||||
// - name: nginx
|
||||
// image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
}
|
||||
|
||||
// ExampleAdd demonstrates adding a setter reference to fields.
|
||||
func ExampleAdd_fieldName() {
|
||||
deployment := `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
something: 3
|
||||
spec:
|
||||
replicas: 3
|
||||
`
|
||||
|
||||
object := yaml.MustParse(deployment) // parse the configuration
|
||||
err := object.PipeE(&Add{
|
||||
Ref: "#/definitions/io.k8s.cli.setters.replicas",
|
||||
FieldName: "replicas",
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Print the object with the update value
|
||||
fmt.Println(object.MustString())
|
||||
|
||||
// Output:
|
||||
// apiVersion: apps/v1
|
||||
// kind: Deployment
|
||||
// metadata:
|
||||
// name: nginx-deployment
|
||||
// annotations:
|
||||
// something: 3
|
||||
// spec:
|
||||
// replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
|
||||
}
|
||||
|
||||
// ExampleAdd demonstrates adding a setter reference to fields.
|
||||
func ExampleAdd_fieldValue() {
|
||||
deployment := `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
something: 3
|
||||
spec:
|
||||
replicas: 3
|
||||
`
|
||||
|
||||
object := yaml.MustParse(deployment) // parse the configuration
|
||||
err := object.PipeE(&Add{
|
||||
Ref: "#/definitions/io.k8s.cli.setters.replicas",
|
||||
FieldValue: "3",
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Print the object with the update value
|
||||
fmt.Println(object.MustString())
|
||||
|
||||
// Output:
|
||||
// apiVersion: apps/v1
|
||||
// kind: Deployment
|
||||
// metadata:
|
||||
// name: nginx-deployment
|
||||
// annotations:
|
||||
// something: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
|
||||
// spec:
|
||||
// replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
|
||||
}
|
||||
117
kyaml/setters2/list.go
Normal file
117
kyaml/setters2/list.go
Normal file
@@ -0,0 +1,117 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package setters2
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// List lists the setters specified in the OpenAPI
|
||||
type List struct {
|
||||
Name string
|
||||
|
||||
Setters []SetterDefinition
|
||||
}
|
||||
|
||||
// List initializes l.Setters with the setters from the OpenAPI definitions in the file
|
||||
func (l *List) List(openAPIPath, resourcePath string) error {
|
||||
if err := openapi.AddSchemaFromFile(openAPIPath); err != nil {
|
||||
return err
|
||||
}
|
||||
y, err := yaml.ReadFile(openAPIPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return l.list(y, resourcePath)
|
||||
}
|
||||
|
||||
func (l *List) list(object *yaml.RNode, resourcePath string) error {
|
||||
// read the OpenAPI definitions
|
||||
def, err := object.Pipe(yaml.LookupCreate(yaml.MappingNode, "openAPI", "definitions"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if yaml.IsEmpty(def) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// iterate over definitions -- find those that are setters
|
||||
err = def.VisitFields(func(node *yaml.MapNode) error {
|
||||
setter := SetterDefinition{}
|
||||
|
||||
// the definition key -- contains the setter name
|
||||
key := node.Key.YNode().Value
|
||||
|
||||
if !strings.HasPrefix(key, SetterDefinitionPrefix) {
|
||||
// not a setter -- doesn't have the right prefix
|
||||
return nil
|
||||
}
|
||||
|
||||
setterNode, err := node.Value.Pipe(yaml.Lookup(K8sCliExtensionKey, "setter"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if yaml.IsEmpty(setterNode) {
|
||||
// has the setter prefix, but missing the setter extension
|
||||
return errors.Errorf("missing x-k8s-cli.setter for %s", key)
|
||||
}
|
||||
|
||||
// unmarshal the yaml for the setter extension into the definition struct
|
||||
b, err := setterNode.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := yaml.Unmarshal([]byte(b), &setter); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if l.Name != "" && l.Name != setter.Name {
|
||||
// not the setter that was requested by list
|
||||
return nil
|
||||
}
|
||||
|
||||
// the description is not part of the extension, and should be pulled out
|
||||
// separately from the extension values.
|
||||
description := node.Value.Field("description")
|
||||
if description != nil {
|
||||
setter.Description = description.Value.YNode().Value
|
||||
}
|
||||
|
||||
// count the number of fields set by this setter
|
||||
setter.Count, err = l.count(resourcePath, setter.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l.Setters = append(l.Setters, setter)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// sort the setters by their name
|
||||
sort.Slice(l.Setters, func(i, j int) bool {
|
||||
return l.Setters[i].Name < l.Setters[j].Name
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// count returns the number of fields set by the setter with name
|
||||
func (l *List) count(path, name string) (int, error) {
|
||||
s := &Set{Name: name}
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{&kio.LocalPackageReader{PackagePath: path}},
|
||||
Filters: []kio.Filter{kio.FilterAll(s)},
|
||||
}.Execute()
|
||||
|
||||
return s.Count, err
|
||||
}
|
||||
289
kyaml/setters2/list_test.go
Normal file
289
kyaml/setters2/list_test.go
Normal file
@@ -0,0 +1,289 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package setters2
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
)
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
setter string
|
||||
openapi string
|
||||
input string
|
||||
expected []SetterDefinition
|
||||
}{
|
||||
{
|
||||
name: "list-replicas",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
setBy: me
|
||||
description: "hello world"
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`,
|
||||
expected: []SetterDefinition{
|
||||
{Name: "replicas", Value: "3", SetBy: "me", Description: "hello world", Count: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list-multiple",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
description: "hello world 1"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
setBy: me1
|
||||
io.k8s.cli.setters.image:
|
||||
description: "hello world 2"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image
|
||||
value: "nginx"
|
||||
setBy: me2
|
||||
io.k8s.cli.setters.tag:
|
||||
description: "hello world 3"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: tag
|
||||
value: "1.7.9"
|
||||
setBy: me3
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: IMAGE:TAG
|
||||
values:
|
||||
- marker: IMAGE
|
||||
ref: '#/definitions/io.k8s.cli.setters.image'
|
||||
- marker: TAG
|
||||
ref: '#/definitions/io.k8s.cli.setters.tag'
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: nginx2
|
||||
image: nginx # {"$ref": "#/definitions/io.k8s.cli.setters.image"}
|
||||
`,
|
||||
expected: []SetterDefinition{
|
||||
{Name: "image", Value: "nginx", SetBy: "me2", Description: "hello world 2", Count: 2},
|
||||
{Name: "replicas", Value: "3", SetBy: "me1", Description: "hello world 1", Count: 1},
|
||||
{Name: "tag", Value: "1.7.9", SetBy: "me3", Description: "hello world 3", Count: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list-multiple-resources",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
description: "hello world 1"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
setBy: me1
|
||||
io.k8s.cli.setters.image:
|
||||
description: "hello world 2"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image
|
||||
value: "nginx"
|
||||
setBy: me2
|
||||
io.k8s.cli.setters.tag:
|
||||
description: "hello world 3"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: tag
|
||||
value: "1.7.9"
|
||||
setBy: me3
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: IMAGE:TAG
|
||||
values:
|
||||
- marker: IMAGE
|
||||
ref: '#/definitions/io.k8s.cli.setters.image'
|
||||
- marker: TAG
|
||||
ref: '#/definitions/io.k8s.cli.setters.tag'
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment-1
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: nginx2
|
||||
image: nginx # {"$ref": "#/definitions/io.k8s.cli.setters.image"}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment-2
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: nginx2
|
||||
image: nginx
|
||||
`,
|
||||
expected: []SetterDefinition{
|
||||
{Name: "image", Value: "nginx", SetBy: "me2", Description: "hello world 2", Count: 3},
|
||||
{Name: "replicas", Value: "3", SetBy: "me1", Description: "hello world 1", Count: 2},
|
||||
{Name: "tag", Value: "1.7.9", SetBy: "me3", Description: "hello world 3", Count: 2},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list-name",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
description: "hello world 1"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
setBy: me1
|
||||
io.k8s.cli.setters.image:
|
||||
description: "hello world 2"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image
|
||||
value: "nginx"
|
||||
setBy: me2
|
||||
io.k8s.cli.setters.tag:
|
||||
description: "hello world 3"
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: tag
|
||||
value: "1.7.9"
|
||||
setBy: me3
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: IMAGE:TAG
|
||||
values:
|
||||
- marker: IMAGE
|
||||
ref: '#/definitions/io.k8s.cli.setters.image'
|
||||
- marker: TAG
|
||||
ref: '#/definitions/io.k8s.cli.setters.tag'
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment-1
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: nginx2
|
||||
image: nginx # {"$ref": "#/definitions/io.k8s.cli.setters.image"}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment-2
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
- name: nginx2
|
||||
image: nginx
|
||||
`,
|
||||
setter: "image",
|
||||
expected: []SetterDefinition{
|
||||
{Name: "image", Value: "nginx", SetBy: "me2", Description: "hello world 2", Count: 3},
|
||||
},
|
||||
},
|
||||
}
|
||||
for i := range tests {
|
||||
test := tests[i]
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
// reset the openAPI afterward
|
||||
defer openapi.ResetOpenAPI()
|
||||
initSchema(t, test.openapi)
|
||||
|
||||
f, err := ioutil.TempFile("", "k8s-cli-")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
err = ioutil.WriteFile(f.Name(), []byte(test.openapi), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
r, err := ioutil.TempFile("", "k8s-cli-*.yaml")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
defer os.Remove(r.Name())
|
||||
err = ioutil.WriteFile(r.Name(), []byte(test.input), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// invoke the setter
|
||||
instance := &List{Name: test.setter}
|
||||
err = instance.List(f.Name(), r.Name())
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if !assert.Equal(t, test.expected, instance.Setters) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
171
kyaml/setters2/set.go
Normal file
171
kyaml/setters2/set.go
Normal file
@@ -0,0 +1,171 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package setters2
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// Set sets resource field values from an OpenAPI setter
|
||||
type Set struct {
|
||||
// Name is the name of the setter to set on the object. i.e. matches the x-k8s-cli.setter.name
|
||||
// of the setter that should have its value applied to fields which reference it.
|
||||
Name string
|
||||
|
||||
// Count is the number of fields that were updated by calling Filter
|
||||
Count int
|
||||
}
|
||||
|
||||
// Filter implements Set as a yaml.Filter
|
||||
func (s *Set) Filter(object *yaml.RNode) (*yaml.RNode, error) {
|
||||
return object, accept(s, object)
|
||||
}
|
||||
|
||||
// visitScalar
|
||||
func (s *Set) visitScalar(object *yaml.RNode, p string) error {
|
||||
// get the openAPI for this field describing how to apply the setter
|
||||
ext, err := getExtFromComment(object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ext == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// perform a direct set of the field if it matches
|
||||
if s.set(object, ext) {
|
||||
s.Count++
|
||||
return nil
|
||||
}
|
||||
|
||||
// perform a substitution of the field if it matches
|
||||
sub, err := s.substitute(object, ext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if sub {
|
||||
s.Count++
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// substitute updates the value of field from ext if ext contains a substitution that
|
||||
// depends on a setter whose name matches s.Name.
|
||||
func (s *Set) substitute(field *yaml.RNode, ext *cliExtension) (bool, error) {
|
||||
nameMatch := false
|
||||
|
||||
// check partial setters to see if they contain the setter as part of a
|
||||
// substitution
|
||||
if ext.Substitution == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
p := ext.Substitution.Pattern
|
||||
|
||||
// substitute each setter into the pattern to get the new value
|
||||
for _, v := range ext.Substitution.Values {
|
||||
if v.Ref == "" {
|
||||
return false, errors.Errorf(
|
||||
"missing reference on substitution " + ext.Substitution.Name)
|
||||
}
|
||||
ref, err := spec.NewRef(v.Ref)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err)
|
||||
}
|
||||
setter, err := openapi.Resolve(&ref) // resolve the setter to its openAPI def
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err)
|
||||
}
|
||||
subSetter, err := getExtFromSchema(setter) // parse the extension out of the openAPI
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err)
|
||||
}
|
||||
// substitute the setters current value into the substitution pattern
|
||||
p = strings.ReplaceAll(p, v.Marker, subSetter.Setter.Value)
|
||||
|
||||
if subSetter.Setter.Name == s.Name {
|
||||
// the substitution depends on the specified setter
|
||||
nameMatch = true
|
||||
}
|
||||
}
|
||||
if !nameMatch {
|
||||
// doesn't depend on the setter, don't modify its value
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// TODO(pwittrock): validate the field value
|
||||
|
||||
field.YNode().Value = p
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// set applies the value from ext to field if its name matches s.Name
|
||||
func (s *Set) set(field *yaml.RNode, ext *cliExtension) bool {
|
||||
// check full setter
|
||||
if ext.Setter == nil || ext.Setter.Name != s.Name {
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO(pwittrock): validate the field value
|
||||
|
||||
// this has a full setter, set its value
|
||||
field.YNode().Value = ext.Setter.Value
|
||||
return true
|
||||
}
|
||||
|
||||
// SetOpenAPI updates a setter value
|
||||
type SetOpenAPI struct {
|
||||
// Name is the name of the setter to add
|
||||
Name string `yaml:"name"`
|
||||
// Value is the current value of the setter
|
||||
Value string `yaml:"value"`
|
||||
|
||||
Description string `yaml:"description"`
|
||||
|
||||
SetBy string `yaml:"setBy"`
|
||||
}
|
||||
|
||||
// UpdateFile updates the OpenAPI definitions in a file with the given setter value.
|
||||
func (s SetOpenAPI) UpdateFile(path string) error {
|
||||
return yaml.UpdateFile(s, path)
|
||||
}
|
||||
|
||||
func (s SetOpenAPI) Filter(object *yaml.RNode) (*yaml.RNode, error) {
|
||||
key := SetterDefinitionPrefix + s.Name
|
||||
def, err := object.Pipe(yaml.Lookup(
|
||||
"openAPI", "definitions", key, "x-k8s-cli", "setter"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if def == nil {
|
||||
return nil, errors.Errorf("no setter %s found", s.Name)
|
||||
}
|
||||
if err := def.PipeE(&yaml.FieldSetter{Name: "value", StringValue: s.Value}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if s.SetBy != "" {
|
||||
if err := def.PipeE(&yaml.FieldSetter{Name: "setBy", StringValue: s.SetBy}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if s.Description != "" {
|
||||
d, err := object.Pipe(yaml.LookupCreate(
|
||||
yaml.MappingNode, "openAPI", "definitions", key))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := d.PipeE(&yaml.FieldSetter{Name: "description", StringValue: s.Description}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return object, nil
|
||||
}
|
||||
655
kyaml/setters2/set_test.go
Normal file
655
kyaml/setters2/set_test.go
Normal file
@@ -0,0 +1,655 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package setters2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
func TestSet_Filter(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
setter string
|
||||
openapi string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "set-replicas",
|
||||
setter: "replicas",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.no-match-1':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-1
|
||||
value: "1"
|
||||
io.k8s.cli.setters.replicas:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "4"
|
||||
io.k8s.cli.setters.no-match-2':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-2
|
||||
value: "2"
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 4 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "set-arg",
|
||||
setter: "arg1",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.replicas:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "4"
|
||||
io.k8s.cli.setters.arg1:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: arg1
|
||||
value: "some value"
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
args:
|
||||
- a
|
||||
- b # {"$ref": "#/definitions/io.k8s.cli.setters.arg1"}
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
args:
|
||||
- a
|
||||
- some value # {"$ref": "#/definitions/io.k8s.cli.setters.arg1"}`,
|
||||
},
|
||||
{
|
||||
name: "substitute-image-tag",
|
||||
setter: "image-tag",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.image-name:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image-name
|
||||
value: "nginx"
|
||||
io.k8s.cli.setters.image-tag:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image-tag
|
||||
value: "1.8.1"
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: IMAGE_NAME:IMAGE_TAG
|
||||
values:
|
||||
- marker: "IMAGE_NAME"
|
||||
ref: "#/definitions/io.k8s.cli.setters.image-name"
|
||||
- marker: "IMAGE_TAG"
|
||||
ref: "#/definitions/io.k8s.cli.setters.image-tag"
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "substitute-annotation",
|
||||
setter: "project",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.project:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: project
|
||||
value: "a"
|
||||
io.k8s.cli.setters.location:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: location
|
||||
value: "b"
|
||||
io.k8s.cli.setters.cluster:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: cluster
|
||||
value: "c"
|
||||
io.k8s.cli.substitutions.key:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: key
|
||||
pattern: https://container.googleapis.com/v1/projects/PROJECT/locations/LOCATION/clusters/CLUSTER
|
||||
values:
|
||||
- marker: "PROJECT"
|
||||
ref: "#/definitions/io.k8s.cli.setters.project"
|
||||
- marker: "LOCATION"
|
||||
ref: "#/definitions/io.k8s.cli.setters.location"
|
||||
- marker: "CLUSTER"
|
||||
ref: "#/definitions/io.k8s.cli.setters.cluster"
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
key: 'https://container.googleapis.com/v1/projects/a/locations/a/clusters/a' # {"$ref": "#/definitions/io.k8s.cli.substitutions.key"}
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
key: 'https://container.googleapis.com/v1/projects/a/locations/b/clusters/c' # {"$ref": "#/definitions/io.k8s.cli.substitutions.key"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "substitute-not-match-setter",
|
||||
setter: "not-real",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.project:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: project
|
||||
value: "a"
|
||||
io.k8s.cli.setters.location:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: location
|
||||
value: "b"
|
||||
io.k8s.cli.setters.cluster:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: cluster
|
||||
value: "c"
|
||||
io.k8s.cli.substitutions.key:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: key
|
||||
pattern: https://container.googleapis.com/v1/projects/PROJECT/locations/LOCATION/clusters/CLUSTER
|
||||
values:
|
||||
- marker: "PROJECT"
|
||||
ref: "#/definitions/io.k8s.cli.setters.project"
|
||||
- marker: "LOCATION"
|
||||
ref: "#/definitions/io.k8s.cli.setters.location"
|
||||
- marker: "CLUSTER"
|
||||
ref: "#/definitions/io.k8s.cli.setters.cluster"
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
key: 'https://container.googleapis.com/v1/projects/a/locations/a/clusters/a' # {"$ref": "#/definitions/io.k8s.cli.substitutions.key"}
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
key: 'https://container.googleapis.com/v1/projects/a/locations/a/clusters/a' # {"$ref": "#/definitions/io.k8s.cli.substitutions.key"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "substitute-image-name",
|
||||
setter: "image-name",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.image-name:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image-name
|
||||
value: "foo"
|
||||
io.k8s.cli.setters.image-tag:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image-tag
|
||||
value: "1.7.9"
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: IMAGE_NAME:IMAGE_TAG
|
||||
values:
|
||||
- marker: "IMAGE_NAME"
|
||||
ref: "#/definitions/io.k8s.cli.setters.image-name"
|
||||
- marker: "IMAGE_TAG"
|
||||
ref: "#/definitions/io.k8s.cli.setters.image-tag"
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: foo:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "substitute-substring",
|
||||
setter: "image-tag",
|
||||
openapi: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.image-name:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image-name
|
||||
value: "nginx"
|
||||
io.k8s.cli.setters.image-tag:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image-tag
|
||||
value: "1.8.1"
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: IMAGE_NAME:IMAGE_TAG
|
||||
values:
|
||||
- marker: "IMAGE_NAME"
|
||||
ref: "#/definitions/io.k8s.cli.setters.image-name"
|
||||
- marker: "IMAGE_TAG"
|
||||
ref: "#/definitions/io.k8s.cli.setters.image-tag"
|
||||
`,
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: a:a # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
`,
|
||||
},
|
||||
}
|
||||
for i := range tests {
|
||||
test := tests[i]
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
// reset the openAPI afterward
|
||||
defer openapi.ResetOpenAPI()
|
||||
initSchema(t, test.openapi)
|
||||
|
||||
// parse the input to be modified
|
||||
r, err := yaml.Parse(test.input)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// invoke the setter
|
||||
instance := &Set{Name: test.setter}
|
||||
result, err := instance.Filter(r)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// compare the actual and expected output
|
||||
actual, err := result.String()
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
actual = strings.TrimSpace(actual)
|
||||
expected := strings.TrimSpace(test.expected)
|
||||
if !assert.Equal(t, expected, actual) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// initSchema initializes the openAPI with the definitions from s
|
||||
func initSchema(t *testing.T, s string) {
|
||||
// parse out the schema from the input openAPI
|
||||
y, err := yaml.Parse(s)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
// get the field containing the openAPI
|
||||
f := y.Field("openAPI")
|
||||
if !assert.NotNil(t, f) {
|
||||
t.FailNow()
|
||||
}
|
||||
defs, err := f.Value.String()
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// convert the yaml openAPI to an interface{}
|
||||
// which can be marshalled into json
|
||||
var o interface{}
|
||||
err = yaml.Unmarshal([]byte(defs), &o)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// convert the interface{} into a json string
|
||||
j, err := json.Marshal(o)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// reset the openAPI to clear existing definitions
|
||||
openapi.ResetOpenAPI()
|
||||
|
||||
// add the json schema to the global schema
|
||||
_, err = openapi.AddSchema(j)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetOpenAPI_Filter(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
setter string
|
||||
value string
|
||||
input string
|
||||
expected string
|
||||
description string
|
||||
setBy string
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "set-replicas",
|
||||
setter: "replicas",
|
||||
value: "3",
|
||||
input: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.no-match-1':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-1
|
||||
value: "1"
|
||||
io.k8s.cli.setters.replicas:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "4"
|
||||
io.k8s.cli.setters.no-match-2':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-2
|
||||
value: "2"
|
||||
`,
|
||||
expected: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.no-match-1':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-1
|
||||
value: "1"
|
||||
io.k8s.cli.setters.replicas:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
io.k8s.cli.setters.no-match-2':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-2
|
||||
value: "2"
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "set-replicas-description",
|
||||
setter: "replicas",
|
||||
value: "3",
|
||||
description: "hello world",
|
||||
input: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.no-match-1':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-1
|
||||
value: "1"
|
||||
io.k8s.cli.setters.replicas:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "4"
|
||||
io.k8s.cli.setters.no-match-2':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-2
|
||||
value: "2"
|
||||
`,
|
||||
expected: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.no-match-1':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-1
|
||||
value: "1"
|
||||
io.k8s.cli.setters.replicas:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
description: hello world
|
||||
io.k8s.cli.setters.no-match-2':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-2
|
||||
value: "2"
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "set-replicas-set-by",
|
||||
setter: "replicas",
|
||||
value: "3",
|
||||
setBy: "carl",
|
||||
input: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.no-match-1':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-1
|
||||
value: "1"
|
||||
io.k8s.cli.setters.replicas:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "4"
|
||||
io.k8s.cli.setters.no-match-2':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-2
|
||||
value: "2"
|
||||
`,
|
||||
expected: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.no-match-1':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-1
|
||||
value: "1"
|
||||
io.k8s.cli.setters.replicas:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: replicas
|
||||
value: "3"
|
||||
setBy: carl
|
||||
io.k8s.cli.setters.no-match-2':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-2
|
||||
value: "2"
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "error",
|
||||
setter: "replicas",
|
||||
err: "no setter replicas found",
|
||||
input: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.no-match-1':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-1
|
||||
value: "1"
|
||||
io.k8s.cli.setters.no-match-2':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-2
|
||||
value: "2"
|
||||
`,
|
||||
expected: `
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.no-match-1':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-1
|
||||
value: "1"
|
||||
io.k8s.cli.setters.no-match-2':
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: no-match-2
|
||||
value: "2"
|
||||
`,
|
||||
},
|
||||
}
|
||||
for i := range tests {
|
||||
test := tests[i]
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
in, err := yaml.Parse(test.input)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// invoke the setter
|
||||
instance := &SetOpenAPI{
|
||||
Name: test.setter, Value: test.value,
|
||||
SetBy: test.setBy, Description: test.description}
|
||||
result, err := instance.Filter(in)
|
||||
if test.err != "" {
|
||||
if !assert.EqualError(t, err, test.err) {
|
||||
t.FailNow()
|
||||
}
|
||||
return
|
||||
}
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// compare the actual and expected output
|
||||
actual, err := result.String()
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
actual = strings.TrimSpace(actual)
|
||||
expected := strings.TrimSpace(test.expected)
|
||||
if !assert.Equal(t, expected, actual) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
48
kyaml/setters2/settersutil/fieldsetter.go
Normal file
48
kyaml/setters2/settersutil/fieldsetter.go
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package settersutil
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters2"
|
||||
)
|
||||
|
||||
// FieldSetter sets the value for a field setter.
|
||||
type FieldSetter struct {
|
||||
// Name is the name of the setter to set
|
||||
Name string
|
||||
|
||||
// Value is the value to set
|
||||
Value string
|
||||
|
||||
Description string
|
||||
|
||||
SetBy string
|
||||
}
|
||||
|
||||
// Set updates the OpenAPI definitions and resources with the new setter value
|
||||
func (fs FieldSetter) Set(openAPIPath, resourcesPath string) (int, error) {
|
||||
// Update the OpenAPI definitions
|
||||
soa := setters2.SetOpenAPI{
|
||||
Name: fs.Name, Value: fs.Value, Description: fs.Description, SetBy: fs.SetBy}
|
||||
if err := soa.UpdateFile(openAPIPath); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Load the updated definitions
|
||||
if err := openapi.AddSchemaFromFile(openAPIPath); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Update the resources with the new value
|
||||
inout := &kio.LocalPackageReadWriter{PackagePath: resourcesPath}
|
||||
s := &setters2.Set{Name: fs.Name}
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{inout},
|
||||
Filters: []kio.Filter{kio.FilterAll(s)},
|
||||
Outputs: []kio.Writer{inout},
|
||||
}.Execute()
|
||||
return s.Count, err
|
||||
}
|
||||
60
kyaml/setters2/settersutil/settercreator.go
Normal file
60
kyaml/setters2/settersutil/settercreator.go
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package settersutil
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters2"
|
||||
)
|
||||
|
||||
// SetterCreator creates or updates a setter in the OpenAPI definitions, and inserts references
|
||||
// to the setter from matching resource fields.
|
||||
type SetterCreator struct {
|
||||
// Name is the name of the setter to create or update.
|
||||
Name string
|
||||
|
||||
Description string
|
||||
|
||||
SetBy string
|
||||
|
||||
// FieldName if set will add the OpenAPI reference to fields with this name or path
|
||||
// FieldName may be the full name of the field, full path to the field, or the path suffix.
|
||||
// e.g. all of the following would match spec.template.spec.containers.image --
|
||||
// [image, containers.image, spec.containers.image, template.spec.containers.image,
|
||||
// spec.template.spec.containers.image]
|
||||
// Optional. If unspecified match all field names.
|
||||
FieldName string
|
||||
|
||||
// FieldValue if set will add the OpenAPI reference to fields if they have this value.
|
||||
// Optional. If unspecified match all field values.
|
||||
FieldValue string
|
||||
}
|
||||
|
||||
func (c SetterCreator) Create(openAPIPath, resourcesPath string) error {
|
||||
// Update the OpenAPI definitions to hace the setter
|
||||
sd := setters2.SetterDefinition{
|
||||
Name: c.Name, Value: c.FieldValue, Description: c.Description, SetBy: c.SetBy}
|
||||
if err := sd.AddToFile(openAPIPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load the updated definitions
|
||||
if err := openapi.AddSchemaFromFile(openAPIPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update the resources with the setter reference
|
||||
inout := &kio.LocalPackageReadWriter{PackagePath: resourcesPath}
|
||||
return kio.Pipeline{
|
||||
Inputs: []kio.Reader{inout},
|
||||
Filters: []kio.Filter{kio.FilterAll(
|
||||
&setters2.Add{
|
||||
FieldName: c.FieldName,
|
||||
FieldValue: c.FieldValue,
|
||||
Ref: setters2.DefinitionsPrefix + setters2.SetterDefinitionPrefix + c.Name,
|
||||
})},
|
||||
Outputs: []kio.Writer{inout},
|
||||
}.Execute()
|
||||
}
|
||||
64
kyaml/setters2/settersutil/substitutioncreator.go
Normal file
64
kyaml/setters2/settersutil/substitutioncreator.go
Normal file
@@ -0,0 +1,64 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package settersutil
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters2"
|
||||
)
|
||||
|
||||
// SubstitutionCreator creates or updates a substitution in the OpenAPI definitions, and
|
||||
// inserts references to the substitution from matching resource fields.
|
||||
type SubstitutionCreator struct {
|
||||
// Name is the name of the substitution to create
|
||||
Name string
|
||||
|
||||
// Pattern is the substitution pattern
|
||||
Pattern string
|
||||
|
||||
// Values are the substitution values for the pattern
|
||||
Values []setters2.Value
|
||||
|
||||
// FieldName if set will add the OpenAPI reference to fields with this name or path
|
||||
// FieldName may be the full name of the field, full path to the field, or the path suffix.
|
||||
// e.g. all of the following would match spec.template.spec.containers.image --
|
||||
// [image, containers.image, spec.containers.image, template.spec.containers.image,
|
||||
// spec.template.spec.containers.image]
|
||||
// Optional. If unspecified match all field names.
|
||||
FieldName string
|
||||
|
||||
// FieldValue if set will add the OpenAPI reference to fields if they have this value.
|
||||
// Optional. If unspecified match all field values.
|
||||
FieldValue string
|
||||
}
|
||||
|
||||
func (c SubstitutionCreator) Create(openAPIPath, resourcesPath string) error {
|
||||
d := setters2.SubstitutionDefinition{
|
||||
Name: c.Name,
|
||||
Values: c.Values,
|
||||
Pattern: c.Pattern,
|
||||
}
|
||||
if err := d.AddToFile(openAPIPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load the updated definitions
|
||||
if err := openapi.AddSchemaFromFile(openAPIPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update the resources with the setter reference
|
||||
inout := &kio.LocalPackageReadWriter{PackagePath: resourcesPath}
|
||||
return kio.Pipeline{
|
||||
Inputs: []kio.Reader{inout},
|
||||
Filters: []kio.Filter{kio.FilterAll(
|
||||
&setters2.Add{
|
||||
FieldName: c.FieldName,
|
||||
FieldValue: c.FieldValue,
|
||||
Ref: setters2.DefinitionsPrefix + setters2.SubstitutionDefinitionPrefix + c.Name,
|
||||
})},
|
||||
Outputs: []kio.Writer{inout},
|
||||
}.Execute()
|
||||
}
|
||||
87
kyaml/setters2/types.go
Normal file
87
kyaml/setters2/types.go
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package setters2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
type cliExtension struct {
|
||||
Setter *setter `yaml:"setter,omitempty" json:"setter,omitempty"`
|
||||
Substitution *substitution `yaml:"substitution,omitempty" json:"substitution,omitempty"`
|
||||
}
|
||||
|
||||
type setter struct {
|
||||
Name string `yaml:"name,omitempty" json:"name,omitempty"`
|
||||
Value string `yaml:"value,omitempty" json:"value,omitempty"`
|
||||
}
|
||||
|
||||
type substitution struct {
|
||||
Name string `yaml:"name,omitempty" json:"name,omitempty"`
|
||||
Pattern string `yaml:"pattern,omitempty" json:"pattern,omitempty"`
|
||||
Values []substitutionSetterReference `yaml:"values,omitempty" json:"values,omitempty"`
|
||||
}
|
||||
|
||||
type substitutionSetterReference struct {
|
||||
Ref string `yaml:"ref,omitempty" json:"ref,omitempty"`
|
||||
Marker string `yaml:"marker,omitempty" json:"marker,omitempty"`
|
||||
}
|
||||
|
||||
//K8sCliExtensionKey is the name of the OpenAPI field containing the setter extensions
|
||||
const K8sCliExtensionKey = "x-k8s-cli"
|
||||
|
||||
// getExtFromSchema returns the cliExtension openAPI extension if it is present in schema
|
||||
func getExtFromSchema(schema *spec.Schema) (*cliExtension, error) {
|
||||
cep := schema.VendorExtensible.Extensions[K8sCliExtensionKey]
|
||||
if cep == nil {
|
||||
return nil, nil
|
||||
}
|
||||
b, err := json.Marshal(cep)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
val := &cliExtension{}
|
||||
if err := json.Unmarshal(b, val); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// getExtFromComment returns the cliExtension openAPI extension if it is present as
|
||||
// a comment on the field.
|
||||
func getExtFromComment(object *yaml.RNode) (*cliExtension, error) {
|
||||
// TODO(pwittrock): also use path to the field to get openapi, not just comments
|
||||
// parse comment containing the extended openapi for this field
|
||||
fm := fieldmeta.FieldMeta{}
|
||||
if err := fm.Read(object); err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
if fm.Schema.Ref.String() == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// resolve the comment reference to the extended openapi definitions
|
||||
r, err := openapi.Resolve(&fm.Schema.Ref)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
if r == nil {
|
||||
// no schema found
|
||||
// TODO(pwittrock): should this be an error if it doesn't resolve?
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// get the cli extension from the openapi (contains setter information)
|
||||
ext, err := getExtFromSchema(r)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
return ext, nil
|
||||
}
|
||||
45
kyaml/setters2/walk.go
Normal file
45
kyaml/setters2/walk.go
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package setters2
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// visitor is implemented by structs which need to walk the configuration.
|
||||
// visitor is provided to accept to walk configuration
|
||||
type visitor interface {
|
||||
// visitScalar is called for each scalar field value on a resource
|
||||
// node is the scalar field value
|
||||
// path is the path to the field; path elements are separated by '.'
|
||||
visitScalar(node *yaml.RNode, path string) error
|
||||
}
|
||||
|
||||
// accept invokes the appropriate function on v for each field in object
|
||||
func accept(v visitor, object *yaml.RNode) error {
|
||||
return acceptImpl(v, object, "")
|
||||
}
|
||||
|
||||
// acceptImpl implements accept using recursion
|
||||
func acceptImpl(v visitor, object *yaml.RNode, p string) error {
|
||||
switch object.YNode().Kind {
|
||||
case yaml.DocumentNode:
|
||||
// Traverse the child of the document
|
||||
return accept(v, yaml.NewRNode(object.YNode()))
|
||||
case yaml.MappingNode:
|
||||
return object.VisitFields(func(node *yaml.MapNode) error {
|
||||
// Traverse each field value
|
||||
return acceptImpl(v, node.Value, p+"."+node.Key.YNode().Value)
|
||||
})
|
||||
case yaml.SequenceNode:
|
||||
return object.VisitElements(func(node *yaml.RNode) error {
|
||||
// Traverse each list element
|
||||
return acceptImpl(v, node, p)
|
||||
})
|
||||
case yaml.ScalarNode:
|
||||
// Visit the scalar field
|
||||
return v.visitScalar(object, p)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -6,6 +6,7 @@ package yaml
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
@@ -99,6 +100,42 @@ func Parse(value string) (*RNode, error) {
|
||||
return Parser{Value: value}.Filter(nil)
|
||||
}
|
||||
|
||||
// ReadFile parses a single Resource from a yaml file
|
||||
func ReadFile(path string) (*RNode, error) {
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Parse(string(b))
|
||||
}
|
||||
|
||||
// WriteFile writes a single Resource to a yaml file
|
||||
func WriteFile(node *RNode, path string) error {
|
||||
out, err := node.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(path, []byte(out), 0600)
|
||||
}
|
||||
|
||||
// UpdateFile reads the file at path, applies the filter to it, and write the result back.
|
||||
// path must contain a exactly 1 resource (YAML).
|
||||
func UpdateFile(filter Filter, path string) error {
|
||||
// Read the yaml
|
||||
y, err := ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update the yaml
|
||||
if err := y.PipeE(filter); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write the yaml
|
||||
return WriteFile(y, path)
|
||||
}
|
||||
|
||||
// TODO(pwittrock): test this
|
||||
func GetStyle(styles ...string) Style {
|
||||
var style Style
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# kyaml version
|
||||
export kyaml_major=0
|
||||
export kyaml_minor=0
|
||||
export kyaml_patch=10
|
||||
export kyaml_patch=13
|
||||
|
||||
# kstatus version
|
||||
export kstatus_major=0
|
||||
@@ -21,7 +21,7 @@ export api_patch=2
|
||||
# cmd/config version
|
||||
export cmd_config_major=0
|
||||
export cmd_config_minor=0
|
||||
export cmd_config_patch=10
|
||||
export cmd_config_patch=13
|
||||
|
||||
# cmd/kubectl version
|
||||
export cmd_kubectl_major=0
|
||||
|
||||
Reference in New Issue
Block a user