From b37abbf05737a0707655f8c1a2fc2779a1b32fae Mon Sep 17 00:00:00 2001 From: Phillip Wittrock Date: Wed, 1 Jan 2020 22:06:18 -0800 Subject: [PATCH] Refactor `set` - Implement inline setters as OpenAPI extensions - Naming cleanup substitute -> set - Documentation cleanup - Simplify implementation --- cmd/config/configcobra/cmds.go | 4 +- cmd/config/docs/commands/create-setter.md | 93 +++++++ cmd/config/docs/commands/set.md | 87 ++++++ cmd/config/docs/commands/sub.md | 98 ------- cmd/config/docs/commands/subset.md | 167 ----------- cmd/config/go.sum | 33 ++- .../internal/commands/cmdcreatesetter.go | 73 +++++ cmd/config/internal/commands/cmdset.go | 120 ++++++++ cmd/config/internal/commands/cmdsub.go | 162 ----------- cmd/config/internal/commands/cmdsubcreate.go | 93 ------- .../internal/generateddocs/commands/docs.go | 260 ++++-------------- cmd/kubectl/go.sum | 1 + cmd/resource/go.sum | 5 + kyaml/fieldmeta/fieldmeta.go | 75 +++-- kyaml/go.mod | 4 +- kyaml/go.sum | 29 +- kyaml/set/addyaml.go | 105 ------- kyaml/set/dokio.go | 53 ---- kyaml/set/doyaml.go | 155 ----------- kyaml/set/lookupkio.go | 69 ----- kyaml/set/lookupyaml.go | 65 ----- kyaml/set/types.go | 29 -- kyaml/{set => setters}/addkio.go | 16 +- kyaml/setters/addyaml.go | 104 +++++++ kyaml/setters/dokio.go | 44 +++ kyaml/setters/doyaml.go | 120 ++++++++ kyaml/setters/lookupkio.go | 65 +++++ kyaml/setters/lookupyaml.go | 70 +++++ 28 files changed, 934 insertions(+), 1265 deletions(-) create mode 100644 cmd/config/docs/commands/create-setter.md create mode 100644 cmd/config/docs/commands/set.md delete mode 100644 cmd/config/docs/commands/sub.md delete mode 100644 cmd/config/docs/commands/subset.md create mode 100644 cmd/config/internal/commands/cmdcreatesetter.go create mode 100644 cmd/config/internal/commands/cmdset.go delete mode 100644 cmd/config/internal/commands/cmdsub.go delete mode 100644 cmd/config/internal/commands/cmdsubcreate.go delete mode 100644 kyaml/set/addyaml.go delete mode 100644 kyaml/set/dokio.go delete mode 100644 kyaml/set/doyaml.go delete mode 100644 kyaml/set/lookupkio.go delete mode 100644 kyaml/set/lookupyaml.go delete mode 100644 kyaml/set/types.go rename kyaml/{set => setters}/addkio.go (60%) create mode 100644 kyaml/setters/addyaml.go create mode 100644 kyaml/setters/dokio.go create mode 100644 kyaml/setters/doyaml.go create mode 100644 kyaml/setters/lookupkio.go create mode 100644 kyaml/setters/lookupyaml.go diff --git a/cmd/config/configcobra/cmds.go b/cmd/config/configcobra/cmds.go index 3e1126e1d..8c8bfda2f 100644 --- a/cmd/config/configcobra/cmds.go +++ b/cmd/config/configcobra/cmds.go @@ -77,8 +77,8 @@ func NewConfigCommand(name string) *cobra.Command { root.AddCommand(commands.Merge3Command(name)) root.AddCommand(commands.CountCommand(name)) root.AddCommand(commands.RunFnCommand(name)) - root.AddCommand(commands.SubCommand(name)) - root.AddCommand(commands.SubSetCommand(name)) + root.AddCommand(commands.SetCommand(name)) + root.AddCommand(commands.CreateSetterCommand(name)) root.AddCommand(&cobra.Command{ Use: "docs-merge", diff --git a/cmd/config/docs/commands/create-setter.md b/cmd/config/docs/commands/create-setter.md new file mode 100644 index 000000000..ede4974af --- /dev/null +++ b/cmd/config/docs/commands/create-setter.md @@ -0,0 +1,93 @@ +## create-setter + +[Alpha] Create a custom setter for a Resource field + +### Synopsis + +Create a custom setter for a Resource field by inlining OpenAPI as comments. + + DIR + + A directory containing Resource configuration. + + NAME + + The name of the setter to create. + + VALUE + + The current value of the field, or a substring within the field. + +### Creating a Custom Setter + +**Given the YAML:** + + # resource.yaml + apiVersion: v1 + kind: Service + metadata: + ... + spec: + ... + ports: + ... + - name: http + port: 8080 + ... + +**Create a new setter:** + + # create a setter for ports + $ kustomize config set create DIR/ http-port 8080 --type "integer" --field "port" + + Resources fields with a field name matching `--field` and field value matching `VALUE` will + have a line comment added marking this field as settable. + +**Newly modified YAML:** + + # resource.yaml + apiVersion: v1 + kind: Service + metadata: + ... + spec: + ... + ports: + ... + - name: http + port: 8080 # {"type":"integer","x-kustomize":{"partialFieldSetters":[{"name":"http-port","value":"8080"}]}} + ... + + Setters may also be defined directly by editing the yaml and adding the comment. + +Users may not set the field value using the `set` command: + + # change the http-port value to 8081 + $ kustomize config set DIR/ http-port 8081 + +### Using default values + +The default values for a setter may be: + +- valid field values (e.g. `8080` or `008080` for a port) +- invalid values that adhere to the schema (e.g. `0000` for a port) +- values that do not adhere to the schema (e.g. `[PORT]` for port) + +A setter may be for a substring of a full field: + + $ kustomize config set create DIR/ image-tag v1.0.01 --type "string" --field "image" + + image: gcr.io/example/app:v1.0.1 # # {"type":"string","x-kustomize":{"partialFieldSetters":[{"name":"image-tag","value":"v1.0.1"}]}} + +A single field value may have multiple setters applied to it for different parts of the field. + +### Examples + + # create a setter for port fields matching "8080" + kustomize config create-setter DIR/ port 8080 --type "integer" --field port \ + --description "default port used by the app" + + # create a setter for a substring of a field rather than the full field -- e.g. only the + # image tag, not the full image + kustomize config create-setter DIR/ image-tag v1.0.1 --type "string" \ + --field image --description "current stable release" \ No newline at end of file diff --git a/cmd/config/docs/commands/set.md b/cmd/config/docs/commands/set.md new file mode 100644 index 000000000..34a4c0913 --- /dev/null +++ b/cmd/config/docs/commands/set.md @@ -0,0 +1,87 @@ +## set + +[Alpha] Set values on Resources fields values. + +### Synopsis + +Set values on Resources fields. May set either the complete or partial field value. + +`set` identifies setters using field metadata published as OpenAPI extensions. +`set` parses both the Kubernetes OpenAPI, as well OpenAPI published inline in +the configuration as comments. + +`set` maybe be used to: + +- edit configuration programmatically from the cli +- create reusable bundles of configuration with custom setters + + DIR + + A directory containing Resource configuration. + + NAME + + Optional. The name of the setter to perform or display. + + VALUE + + Optional. The value to set on the field. + + +To print the possible setters for the Resources in a directory, run `set` on +a directory -- e.g. `kustomize config set DIR/`. + +#### Tips + +- A description of the value may be specified with `--description`. +- The last setter for the field's value may be defined with `--set-by`. +- Create custom setters on Resources, Kustomization.yaml's, patches, etc + +The description and setBy fields are left unmodified unless specified with flags. + +To create a custom setter for a field see: `kustomize help config create-setter` + +### Examples + + Resource YAML: Name Prefix Setter + + # DIR/resources.yaml + ... + metadata: + name: PREFIX-app1 # {"type":"string","x-kustomize":{"partialFieldSetters":[{"name":"name-prefix","value":"PREFIX"}]}} + ... + --- + ... + metadata: + name: PREFIX-app2 # {"type":"string","x-kustomize":{"partialFieldSetters":[{"name":"name-prefix","value":"PREFIX"}]}} + ... + + List setters: Show the possible setters + + $ config set DIR/ + NAME DESCRIPTION VALUE TYPE COUNT OWNER + name-prefix '' PREFIX string 2 + + Perform substitution: set a new value, owner and description + + $ kustomize config set DIR/ name-prefix "test" --description "test environment" --set-by "dev" + performed 2 substitutions + + Show substitutions: Show the new values + + $ config set dir + NAME DESCRIPTION VALUE TYPE COUNT SUBSTITUTED OWNER + prefix 'test environment' test string 2 true dev + + New Resource YAML: + + # DIR/resources.yaml + ... + metadata: + name: test-app1 # {"description":"test environment","type":"string","x-kustomize":{"setBy":"dev","partialFieldSetters":[{"name":"name-prefix","value":"test"}]}} + ... + --- + ... + metadata: + name: test-app2 # {"description":"test environment","type":"string","x-kustomize":{"setBy":"dev","partialFieldSetters":[{"name":"name-prefix","value":"test"}]}} + ... diff --git a/cmd/config/docs/commands/sub.md b/cmd/config/docs/commands/sub.md deleted file mode 100644 index eb5b7fbc8..000000000 --- a/cmd/config/docs/commands/sub.md +++ /dev/null @@ -1,98 +0,0 @@ -## set - -[Alpha] Set values on Resources fields by substituting values. - -### Synopsis - -Set values on Resources fields by substituting predefined markers for new values. - -`set` looks for markers specified on Resource fields and substitute a new user defined -value for the existing value. - -`set` maybe be used to: - -- edit configuration programmatically from the cli or scripts -- create reusable bundles of configuration - - DIR - - A directory containing Resource configuration. - - NAME - - Optional. The name of the substitution to perform or display. - - VALUE - - Optional. The new value to substitute into the field. - - -To print the possible substitutions for the Resources in a directory, run `set` on -a directory -- e.g. `kustomize config set DIR/`. - -#### Tips - -- A description of the value may be specified with `--description`. -- An owner for the field's value may be defined with `--owned-by`. -- Prevent overriding previous substitutions with `--override=false`. -- Revert previous substitutions with `--revert`. -- Create substitutions on Kustomization.yaml's, patches, etc - -When overriding or reverting previous substitutions, the description and owner are left -unmodified unless specified with flags. - -To create a substitution for a field see: `kustomize help config set create` - -### Examples - - Resource YAML: Name substitution - - # dir/resources.yaml - ... - metadata: - name: PREFIX-app1 # {"substitutions":[{"name":"prefix","marker":"PREFIX-"}]} - ... - --- - ... - metadata: - name: PREFIX-app2 # {"substitutions":[{"name":"prefix","marker":"PREFIX-"}]} - ... - - Show substitutions: Show the possible substitutions - - $ config set dir - NAME DESCRIPTION VALUE TYPE COUNT SUBSTITUTED OWNER - prefix '' PREFIX- string 2 false - - Perform substitution: set a new value, owner and description - - $ config set dir prefix "test-" --description "test environment" --owned-by "dev" - performed 2 substitutions - - Show substitutions: Show the new values - - $ config set dir - NAME DESCRIPTION VALUE TYPE COUNT SUBSTITUTED OWNER - prefix 'test environment' test- string 2 true dev - - New Resource YAML: - - # dir/resources.yaml - ... - metadata: - name: test-app1 # {"substitutions":[{"name":"prefix","marker":"PREFIX-","value":"test-"}],"setBy":"dev","description":"test environment"} - ... - --- - ... - metadata: - name: test-app2 # {"substitutions":[{"name":"prefix","marker":"PREFIX-","value":"test-"}],"setBy":"dev","description":"test environment"} - ... - - Revert substitution: - - config set dir prefix --revert - performed 2 substitutions - - config set dir - NAME DESCRIPTION VALUE TYPE COUNT SUBSTITUTED OWNER - prefix 'test environment' PREFIX- string 2 false dev diff --git a/cmd/config/docs/commands/subset.md b/cmd/config/docs/commands/subset.md deleted file mode 100644 index f5ee8b91d..000000000 --- a/cmd/config/docs/commands/subset.md +++ /dev/null @@ -1,167 +0,0 @@ -## sub-set-marker - -[Alpha] Create a new substitution for a Resource field - -### Synopsis - -Create a new substitution for a Resource field -- recognized by `kustomize config set`. - - DIR - - A directory containing Resource configuration. - - NAME - - The name of the substitution to create. - - VALUE - - The current value of the field, or a substring of the field. - -#### Tips: Picking Good Marker - -Substitutions may be defined by directly editing yaml **or** by running `kustomize config set create` -to create a new substitution. - -Given the YAML: - - # resource.yaml - apiVersion: v1 - kind: Service - metadata: - ... - spec: - ... - ports: - ... - - name: http - port: 8080 - ... - -Create a new set marker: - - # create a substitution for ports - $ kustomize config set create dir/ http-port 8080 --type "int" --field "port" - -Modified YAML: - - # resource.yaml - apiVersion: v1 - kind: Service - metadata: - ... - spec: - ... - ports: - ... - - name: http - port: 8080 # {"substitutions":[{"name":"port","marker":"[MARKER]"}],"type":"int"} - ... - -Change the value using the `set` command: - - # change the http-port value to 8081 - $ kustomize config set dir/ http-port 8081 - -Resources fields with a field name matching `--field` and field value matching `VALUE` will -have a line comment added marking this field as settable. - -Substitution markers may be: - -- valid field values (e.g. `8080` for a port) - - Note: `008080` would be preferred because it is more recognizable as a marker -- invalid values that adhere to the schema (e.g. `0000` for a port) -- values that do not adhere to the schema (e.g. `[PORT]` for port) - -Markers **SHOULD be clearly identifiable as a marker and either**: - -- **adhere to the field schema** -- e.g. use a valid value - - - port: 008080 # {"substitutions":[{"name":"port","marker":"008080"}],"type":"int"} - -- **be pre-filled in with a value** -- e.g. set the value when setting the marker - - - port: 8080 # {"substitutions":[{"name":"port","marker":"[MARKER]","value":"8080""}],"type":"int"} - -**Note:** The important thing is that in both cases the Resource configuration may be directly -applied to a cluster and validated by tools without the tool knowing about the substitution -marker. - -The difference between the preceding examples is that: - -- the former will be shown as `SUBSTITUTED=false` (`config sub dir/` exits non-0) -- the latter with show up as `SUBSTITUTED=true` (`config sub dir/` exits 0) - -When choosing the which to use, consider that checks for unsubstituted values MAY be -configured as pre-commit checks -- if you want to these checks to fail if the value -hasn't been substituted, then don't specify a `value`. - -Markers which are invalid field values MAY be chosen in cases where it is preferred to have -the create or update request fail rather than succeed if the substitution has not yet been -performed. - -A substitution may be a substring of the full field: - - $ kustomize config set create dir/ app-image-tag v1.0.01 --type "string" --field "image" - - image: gcr.io/example/app:v1.0.1 # {"substitutions":[{"name":"app-image-tag","marker":"[MARKER]","value":"v1.0.1"}]} - - -A single field value may have multiple substitutions applied to it: - - name: PREFIX-app-SUFFIX # {"substitutions":[{"name":"prefix","marker":"PREFIX-"},{"name":"suffix","marker":"-SUFFIX"}]} - -#### Substitution Format - -Substitutions are defined as json encoded FieldMeta comments on fields. - -FieldMeta Schema read by `sub`: - - { - "title": "FieldMeta", - "type": "object", - "properties": { - "substitutions": { - "type": "array", - "description": "Possible substitutions that may be performed against this field.", - "items": { - "type": "object", - "properties": { - "name": "Name of the substitution.", - "marker": "Marker for the value to be substituted.", - "value": "Current substituted value" - } - } - }, - "type": { - "type": "string", - "description": "The value type. Defaults to string." - "enum": ["string", "int", "float", "bool"] - }, - "description": { - "type": "string", - "description": "A description of the field's current value. Optional." - }, - "setBy": { - "type": "string", - "description": "The current owner of the field. Optional." - }, - } - } - -### Examples - - # set a substitution for port fields matching "8080" - kustomize config sub create dir/ port 8080 --type "int" --field port \ - --description "default port used by the app" - - # set a substitution for port fields matching "8080", using "0000" as a marker. - kustomize config sub dir/ port 8080 --marker "0000" --type "int" \ - --field port --description "default port used by the app" - - # substitute a substring of a field rather than the full field -- e.g. only the - # image tag, not the full image - kustomize config sub dir/ app-image-tag v1.0.1 --type "string" --substring \ - --field port --description "current stable release" \ No newline at end of file diff --git a/cmd/config/go.sum b/cmd/config/go.sum index 6a2ddd573..4fdd40b6a 100644 --- a/cmd/config/go.sum +++ b/cmd/config/go.sum @@ -1,8 +1,11 @@ -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= @@ -16,16 +19,25 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZ github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/spec v0.19.5 h1:Xm0Ao53uqnk9QE/LlYV5DEU09UAgpliA85QoT9LzqPw= +github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I= github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -43,7 +55,6 @@ github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= @@ -51,21 +62,20 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= -github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -81,9 +91,7 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -94,11 +102,9 @@ github.com/posener/script v1.0.4 h1:nSuXW5ZdmFnQIueLB2s0qvs4oNsUloM1Zydzh75v42w= github.com/posener/script v1.0.4/go.mod h1:Rg3ijooqulo05aGLyGsHoLmIOUzHUVK19WVgrYBPU/E= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -119,6 +125,9 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -128,12 +137,12 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/cmd/config/internal/commands/cmdcreatesetter.go b/cmd/config/internal/commands/cmdcreatesetter.go new file mode 100644 index 000000000..4dc9c5641 --- /dev/null +++ b/cmd/config/internal/commands/cmdcreatesetter.go @@ -0,0 +1,73 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package commands + +import ( + "github.com/spf13/cobra" + "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/setters" +) + +// NewCreateSetterRunner returns a command runner. +func NewCreateSetterRunner(parent string) *CreateSetterRunner { + r := &CreateSetterRunner{} + set := &cobra.Command{ + Use: "create-setter DIR NAME VALUE", + Args: cobra.ExactArgs(3), + Short: commands.CreateSetterShort, + Long: commands.CreateSetterLong, + Example: commands.CreateSetterExamples, + PreRunE: r.preRunE, + RunE: r.runE, + } + set.Flags().StringVar(&r.Set.SetPartialField.SetBy, "set-by", "", + "set the setBy annotation.") + set.Flags().StringVar(&r.Set.SetPartialField.Description, "description", "", + "set the description of the field value.") + set.Flags().StringVar(&r.Set.SetPartialField.Field, "field", "", + "name of the field to set -- e.g. --field port") + set.Flags().StringVar(&r.Set.ResourceMeta.Name, "name", "", + "name of the Resource on which to create the setter.") + set.Flags().StringVar(&r.Set.ResourceMeta.Kind, "kind", "", + "kind of the Resource on which to create the setter.") + set.Flags().StringVar(&r.Set.SetPartialField.Type, "type", "", + "valid OpenAPI field type -- e.g. integer,boolean,string.") + fixDocs(parent, set) + set.MarkFlagRequired("type") + set.MarkFlagRequired("field") + r.Command = set + return r +} + +func CreateSetterCommand(parent string) *cobra.Command { + return NewCreateSetterRunner(parent).Command +} + +type CreateSetterRunner struct { + Command *cobra.Command + Set setters.CreateSetter +} + +func (r *CreateSetterRunner) runE(c *cobra.Command, args []string) error { + return handleError(c, r.set(c, args)) +} + +func (r *CreateSetterRunner) preRunE(c *cobra.Command, args []string) error { + r.Set.SetPartialField.Setter.Name = args[1] + r.Set.SetPartialField.Setter.Value = args[2] + return nil +} + +func (r *CreateSetterRunner) set(c *cobra.Command, args []string) error { + rw := &kio.LocalPackageReadWriter{PackagePath: args[0]} + err := kio.Pipeline{ + Inputs: []kio.Reader{rw}, + Filters: []kio.Filter{&r.Set}, + Outputs: []kio.Writer{rw}}.Execute() + if err != nil { + return err + } + return nil +} diff --git a/cmd/config/internal/commands/cmdset.go b/cmd/config/internal/commands/cmdset.go new file mode 100644 index 000000000..d2ef769fc --- /dev/null +++ b/cmd/config/internal/commands/cmdset.go @@ -0,0 +1,120 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package commands + +import ( + "fmt" + + "github.com/olekukonko/tablewriter" + "github.com/spf13/cobra" + "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/setters" +) + +// NewSetRunner returns a command runner. +func NewSetRunner(parent string) *SetRunner { + r := &SetRunner{} + c := &cobra.Command{ + Use: "set DIR [NAME] [VALUE]", + Args: cobra.RangeArgs(1, 3), + Short: commands.SetShort, + Long: commands.SetLong, + Example: commands.SetExamples, + PreRunE: r.preRunE, + RunE: r.runE, + } + fixDocs(parent, c) + r.Command = c + + return r +} + +func SetCommand(parent string) *cobra.Command { + return NewSetRunner(parent).Command +} + +type SetRunner struct { + Command *cobra.Command + Lookup setters.LookupSetters + Perform setters.PerformSetters +} + +func (r *SetRunner) preRunE(c *cobra.Command, args []string) error { + if len(args) > 1 { + r.Perform.Name = args[1] + r.Lookup.Name = args[1] + } + if len(args) > 2 { + r.Perform.Value = args[2] + } + + return nil +} + +func (r *SetRunner) runE(c *cobra.Command, args []string) error { + + if len(args) == 3 { + return handleError(c, r.perform(c, args)) + } + + return handleError(c, r.lookup(c, args)) +} + +func (r *SetRunner) lookup(c *cobra.Command, args []string) error { + // lookup the setters + err := kio.Pipeline{ + Inputs: []kio.Reader{&kio.LocalPackageReader{PackagePath: args[0]}}, + Filters: []kio.Filter{&r.Lookup}, + }.Execute() + if err != nil { + return err + } + + table := tablewriter.NewWriter(c.OutOrStdout()) + table.SetRowLine(false) + table.SetBorder(false) + table.SetHeaderLine(false) + table.SetColumnSeparator(" ") + table.SetCenterSeparator(" ") + table.SetAlignment(tablewriter.ALIGN_LEFT) + table.SetHeader([]string{ + "NAME", "DESCRIPTION", "VALUE", "TYPE", "COUNT", "SETBY", + }) + for i := range r.Lookup.SetterCounts { + s := r.Lookup.SetterCounts[i] + v := s.Value + if s.Value == "" { + v = s.Value + } + table.Append([]string{ + s.Name, + "'" + s.Description + "'", + v, + fmt.Sprintf("%v", s.Type), + fmt.Sprintf("%d", s.Count), + s.SetBy, + }) + } + table.Render() + return nil +} + +// perform the setters +func (r *SetRunner) perform(c *cobra.Command, args []string) error { + rw := &kio.LocalPackageReadWriter{ + PackagePath: args[0], + } + // perform the setters in the package + err := kio.Pipeline{ + Inputs: []kio.Reader{rw}, + Filters: []kio.Filter{&r.Perform}, + Outputs: []kio.Writer{rw}, + }.Execute() + if err != nil { + return err + } + fmt.Fprintf(c.OutOrStdout(), "set %d fields\n", r.Perform.Count) + return nil +} diff --git a/cmd/config/internal/commands/cmdsub.go b/cmd/config/internal/commands/cmdsub.go deleted file mode 100644 index 8c4c3834d..000000000 --- a/cmd/config/internal/commands/cmdsub.go +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2019 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package commands - -import ( - "fmt" - "os" - - "github.com/olekukonko/tablewriter" - "github.com/spf13/cobra" - "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" - "sigs.k8s.io/kustomize/kyaml/errors" - "sigs.k8s.io/kustomize/kyaml/kio" - "sigs.k8s.io/kustomize/kyaml/set" -) - -// NewSubRunner returns a command runner. -func NewSubRunner(parent string) *SubRunner { - r := &SubRunner{} - c := &cobra.Command{ - Use: "set DIR [NAME] [VALUE]", - Args: cobra.RangeArgs(1, 3), - Short: commands.SubShort, - Long: commands.SubLong, - Example: commands.SubExamples, - Aliases: []string{"sub"}, - PreRunE: r.preRunE, - RunE: r.runE, - } - c.Flags().BoolVar(&r.Perform.Override, "override", true, - "override previously substituted values.") - c.Flags().BoolVar(&r.Perform.Revert, "revert", false, - "override previously substituted values.") - fixDocs(parent, c) - r.Command = c - c.AddCommand(SubSetCommand(parent)) - return r -} - -func SubCommand(parent string) *cobra.Command { - return NewSubRunner(parent).Command -} - -type SubRunner struct { - Command *cobra.Command - Lookup set.LookupSubstitutions - Perform set.PerformSubstitutions -} - -func (r *SubRunner) preRunE(c *cobra.Command, args []string) error { - if len(args) > 1 { - r.Perform.Name = args[1] - r.Lookup.Name = args[1] - } - if len(args) > 2 { - r.Perform.NewValue = args[2] - } - if len(args) < 2 && r.Perform.Revert { - return errors.Errorf("must specify NAME with --revert") - } - - var mutex int - if r.Perform.Revert { - mutex++ - } - if r.Perform.Override { - mutex++ - } - if mutex > 1 { - return errors.Errorf("--revert, --override are mutually exclusive") - } - - return nil -} - -func (r *SubRunner) runE(c *cobra.Command, args []string) error { - - if len(args) == 3 { - return handleError(c, r.perform(c, args)) - } - if len(args) == 2 && r.Perform.Revert { - return handleError(c, r.perform(c, args)) - } - - return handleError(c, r.lookup(c, args)) -} - -func (r *SubRunner) lookup(c *cobra.Command, args []string) error { - // lookup the substitutions - err := kio.Pipeline{ - Inputs: []kio.Reader{&kio.LocalPackageReader{PackagePath: args[0]}}, - Filters: []kio.Filter{&r.Lookup}, - }.Execute() - if err != nil { - return err - } - - remaining := false - table := tablewriter.NewWriter(c.OutOrStdout()) - table.SetRowLine(false) - table.SetBorder(false) - table.SetHeaderLine(false) - table.SetColumnSeparator(" ") - table.SetCenterSeparator(" ") - table.SetAlignment(tablewriter.ALIGN_LEFT) - table.SetHeader([]string{ - "NAME", "DESCRIPTION", "VALUE", "TYPE", "COUNT", "SUBSTITUTED", "OWNER", - }) - for i := range r.Lookup.SubstitutionCounts { - s := r.Lookup.SubstitutionCounts[i] - remaining = remaining || s.Count > s.CountComplete - v := s.CurrentValue - if s.CurrentValue == "" { - v = s.Marker - } - table.Append([]string{ - s.Name, - "'" + s.Description + "'", - v, - fmt.Sprintf("%v", s.Type), - fmt.Sprintf("%d", s.Count), - fmt.Sprintf("%v", s.Count == s.CountComplete), - s.OwnedBy, - }) - } - table.Render() - - if remaining { - os.Exit(1) - } - return nil -} - -// perform the substitutions -func (r *SubRunner) perform(c *cobra.Command, args []string) error { - rw := &kio.LocalPackageReadWriter{ - PackagePath: args[0], - } - // perform the substitutions in the package - err := kio.Pipeline{ - Inputs: []kio.Reader{rw}, - Filters: []kio.Filter{&r.Perform}, - Outputs: []kio.Writer{rw}, - }.Execute() - if err != nil { - return err - } - fmt.Fprintf(c.OutOrStdout(), "performed %d substitutions\n", r.Perform.Count) - return nil -} diff --git a/cmd/config/internal/commands/cmdsubcreate.go b/cmd/config/internal/commands/cmdsubcreate.go deleted file mode 100644 index 680584a95..000000000 --- a/cmd/config/internal/commands/cmdsubcreate.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2019 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package commands - -import ( - "github.com/spf13/cobra" - "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" - "sigs.k8s.io/kustomize/kyaml/kio" - "sigs.k8s.io/kustomize/kyaml/set" -) - -// NewSubSetRunner returns a command runner. -func NewSubSetRunner(parent string) *SubSetRunner { - r := &SubSetRunner{} - set := &cobra.Command{ - Use: "create PKG_DIR NAME [VALUE]", - Args: cobra.ExactArgs(3), - Short: commands.SubsetShort, - Long: commands.SubsetLong, - Example: commands.SubsetExamples, - PreRunE: r.preRunE, - RunE: r.runE, - } - set.Flags().StringVar(&r.Set.Marker.OwnedBy, "owned-by", "", - "set this owner on for the current value.") - set.Flags().StringVar(&r.Set.Marker.Description, "description", "", - "set this description for the current value description.") - set.Flags().StringVar(&r.Set.Marker.Substitution.Marker, "marker", "[MARKER]", - "use this marker.") - set.Flags().StringVar(&r.Set.Marker.Field, "field", "", - "name of the field to set -- e.g. --field port") - set.Flags().StringVar(&r.Set.ResourceMeta.Name, "name", "", - "name of the Resource on which to set the substitution.") - set.Flags().StringVar(&r.Set.ResourceMeta.Kind, "kind", "", - "kind of the Resource on which to set substitution.") - set.Flags().StringVar(&r.Set.Marker.Type, "type", "", - "field type -- e.g. int,float,bool,string.") - set.Flags().BoolVar(&r.Set.Marker.PartialMatch, "substring", false, - "if true, the value may be a substring of the current value.") - fixDocs(parent, set) - set.MarkFlagRequired("type") - set.MarkFlagRequired("field") - r.Command = set - return r -} - -func SubSetCommand(parent string) *cobra.Command { - return NewSubSetRunner(parent).Command -} - -type SubSetRunner struct { - Command *cobra.Command - Set set.SetSubstitutionMarker -} - -func (r *SubSetRunner) runE(c *cobra.Command, args []string) error { - return handleError(c, r.set(c, args)) -} - -func (r *SubSetRunner) preRunE(c *cobra.Command, args []string) error { - r.Set.Marker.Substitution.Name = args[1] - r.Set.Marker.Substitution.Value = args[2] - return nil -} - -// perform the substitutions -func (r *SubSetRunner) set(c *cobra.Command, args []string) error { - rw := &kio.LocalPackageReadWriter{ - PackagePath: args[0], - } - // add the substitution marker to the Resource - err := kio.Pipeline{ - Inputs: []kio.Reader{rw}, - Filters: []kio.Filter{&r.Set}, - Outputs: []kio.Writer{rw}, - }.Execute() - if err != nil { - return err - } - return nil -} diff --git a/cmd/config/internal/generateddocs/commands/docs.go b/cmd/config/internal/generateddocs/commands/docs.go index ece7df4d6..c4d11e567 100644 --- a/cmd/config/internal/generateddocs/commands/docs.go +++ b/cmd/config/internal/generateddocs/commands/docs.go @@ -49,6 +49,32 @@ var CountExamples = ` # print Resource counts from a directory kustomize config count my-dir/` +var CreateSetterShort = `[Alpha] Create a custom setter for a Resource field` +var CreateSetterLong = ` +Create a custom setter for a Resource field by inlining OpenAPI as comments. + + DIR + + A directory containing Resource configuration. + + NAME + + The name of the setter to create. + + VALUE + + The current value of the field, or a substring within the field. +` +var CreateSetterExamples = ` + # create a setter for port fields matching "8080" + kustomize config create-setter DIR/ port 8080 --type "integer" --field port \ + --description "default port used by the app" + + # create a setter for a substring of a field rather than the full field -- e.g. only the + # image tag, not the full image + kustomize config create-setter DIR/ image-tag v1.0.1 --type "string" \ + --field image --description "current stable release"` + var FmtShort = `[Alpha] Format yaml configuration files.` var FmtLong = ` [Alpha] Format yaml configuration files. @@ -204,17 +230,18 @@ order they appear in the file). var RunFnsExamples = ` kustomize config run example/` -var SubShort = `[Alpha] Set values on Resources fields by substituting values.` -var SubLong = ` -Set values on Resources fields by substituting predefined markers for new values. +var SetShort = `[Alpha] Set values on Resources fields values.` +var SetLong = ` +Set values on Resources fields. May set either the complete or partial field value. -` + "`" + `set` + "`" + ` looks for markers specified on Resource fields and substitute a new user defined -value for the existing value. +` + "`" + `set` + "`" + ` identifies setters using field metadata published as OpenAPI extensions. +` + "`" + `set` + "`" + ` parses both the Kubernetes OpenAPI, as well OpenAPI published inline in +the configuration as comments. ` + "`" + `set` + "`" + ` maybe be used to: -- edit configuration programmatically from the cli or scripts -- create reusable bundles of configuration +- edit configuration programmatically from the cli +- create reusable bundles of configuration with custom setters DIR @@ -222,244 +249,69 @@ value for the existing value. NAME - Optional. The name of the substitution to perform or display. + Optional. The name of the setter to perform or display. VALUE - Optional. The new value to substitute into the field. + Optional. The value to set on the field. -To print the possible substitutions for the Resources in a directory, run ` + "`" + `set` + "`" + ` on +To print the possible setters for the Resources in a directory, run ` + "`" + `set` + "`" + ` on a directory -- e.g. ` + "`" + `kustomize config set DIR/` + "`" + `. #### Tips - A description of the value may be specified with ` + "`" + `--description` + "`" + `. -- An owner for the field's value may be defined with ` + "`" + `--owned-by` + "`" + `. -- Prevent overriding previous substitutions with ` + "`" + `--override=false` + "`" + `. -- Revert previous substitutions with ` + "`" + `--revert` + "`" + `. -- Create substitutions on Kustomization.yaml's, patches, etc +- The last setter for the field's value may be defined with ` + "`" + `--set-by` + "`" + `. +- Create custom setters on Resources, Kustomization.yaml's, patches, etc -When overriding or reverting previous substitutions, the description and owner are left -unmodified unless specified with flags. +The description and setBy fields are left unmodified unless specified with flags. -To create a substitution for a field see: ` + "`" + `kustomize help config set create` + "`" + ` +To create a custom setter for a field see: ` + "`" + `kustomize help config create-setter` + "`" + ` ` -var SubExamples = ` - Resource YAML: Name substitution +var SetExamples = ` + Resource YAML: Name Prefix Setter - # dir/resources.yaml + # DIR/resources.yaml ... metadata: - name: PREFIX-app1 # {"substitutions":[{"name":"prefix","marker":"PREFIX-"}]} + name: PREFIX-app1 # {"type":"string","x-kustomize":{"partialFieldSetters":[{"name":"name-prefix","value":"PREFIX"}]}} ... --- ... metadata: - name: PREFIX-app2 # {"substitutions":[{"name":"prefix","marker":"PREFIX-"}]} + name: PREFIX-app2 # {"type":"string","x-kustomize":{"partialFieldSetters":[{"name":"name-prefix","value":"PREFIX"}]}} ... - Show substitutions: Show the possible substitutions + List setters: Show the possible setters - $ config set dir - NAME DESCRIPTION VALUE TYPE COUNT SUBSTITUTED OWNER - prefix '' PREFIX- string 2 false + $ config set DIR/ + NAME DESCRIPTION VALUE TYPE COUNT OWNER + name-prefix '' PREFIX string 2 Perform substitution: set a new value, owner and description - $ config set dir prefix "test-" --description "test environment" --owned-by "dev" + $ kustomize config set DIR/ name-prefix "test" --description "test environment" --set-by "dev" performed 2 substitutions Show substitutions: Show the new values $ config set dir NAME DESCRIPTION VALUE TYPE COUNT SUBSTITUTED OWNER - prefix 'test environment' test- string 2 true dev + prefix 'test environment' test string 2 true dev New Resource YAML: - # dir/resources.yaml + # DIR/resources.yaml ... metadata: - name: test-app1 # {"substitutions":[{"name":"prefix","marker":"PREFIX-","value":"test-"}],"setBy":"dev","description":"test environment"} + name: test-app1 # {"description":"test environment","type":"string","x-kustomize":{"setBy":"dev","partialFieldSetters":[{"name":"name-prefix","value":"test"}]}} ... --- ... metadata: - name: test-app2 # {"substitutions":[{"name":"prefix","marker":"PREFIX-","value":"test-"}],"setBy":"dev","description":"test environment"} - ... - - Revert substitution: - - config set dir prefix --revert - performed 2 substitutions - - config set dir - NAME DESCRIPTION VALUE TYPE COUNT SUBSTITUTED OWNER - prefix 'test environment' PREFIX- string 2 false dev ` - -var SubsetShort = `[Alpha] Create a new substitution for a Resource field` -var SubsetLong = ` -Create a new substitution for a Resource field -- recognized by ` + "`" + `kustomize config set` + "`" + `. - - DIR - - A directory containing Resource configuration. - - NAME - - The name of the substitution to create. - - VALUE - - The current value of the field, or a substring of the field. - -#### Tips: Picking Good Marker - -Substitutions may be defined by directly editing yaml **or** by running ` + "`" + `kustomize config set create` + "`" + ` -to create a new substitution. - -Given the YAML: - - # resource.yaml - apiVersion: v1 - kind: Service - metadata: - ... - spec: - ... - ports: - ... - - name: http - port: 8080 - ... - -Create a new set marker: - - # create a substitution for ports - $ kustomize config set create dir/ http-port 8080 --type "int" --field "port" - -Modified YAML: - - # resource.yaml - apiVersion: v1 - kind: Service - metadata: - ... - spec: - ... - ports: - ... - - name: http - port: 8080 # {"substitutions":[{"name":"port","marker":"[MARKER]"}],"type":"int"} - ... - -Change the value using the ` + "`" + `set` + "`" + ` command: - - # change the http-port value to 8081 - $ kustomize config set dir/ http-port 8081 - -Resources fields with a field name matching ` + "`" + `--field` + "`" + ` and field value matching ` + "`" + `VALUE` + "`" + ` will -have a line comment added marking this field as settable. - -Substitution markers may be: - -- valid field values (e.g. ` + "`" + `8080` + "`" + ` for a port) - - Note: ` + "`" + `008080` + "`" + ` would be preferred because it is more recognizable as a marker -- invalid values that adhere to the schema (e.g. ` + "`" + `0000` + "`" + ` for a port) -- values that do not adhere to the schema (e.g. ` + "`" + `[PORT]` + "`" + ` for port) - -Markers **SHOULD be clearly identifiable as a marker and either**: - -- **adhere to the field schema** -- e.g. use a valid value - - - port: 008080 # {"substitutions":[{"name":"port","marker":"008080"}],"type":"int"} - -- **be pre-filled in with a value** -- e.g. set the value when setting the marker - - - port: 8080 # {"substitutions":[{"name":"port","marker":"[MARKER]","value":"8080""}],"type":"int"} - -**Note:** The important thing is that in both cases the Resource configuration may be directly -applied to a cluster and validated by tools without the tool knowing about the substitution -marker. - -The difference between the preceding examples is that: - -- the former will be shown as ` + "`" + `SUBSTITUTED=false` + "`" + ` (` + "`" + `config sub dir/` + "`" + ` exits non-0) -- the latter with show up as ` + "`" + `SUBSTITUTED=true` + "`" + ` (` + "`" + `config sub dir/` + "`" + ` exits 0) - -When choosing the which to use, consider that checks for unsubstituted values MAY be -configured as pre-commit checks -- if you want to these checks to fail if the value -hasn't been substituted, then don't specify a ` + "`" + `value` + "`" + `. - -Markers which are invalid field values MAY be chosen in cases where it is preferred to have -the create or update request fail rather than succeed if the substitution has not yet been -performed. - -A substitution may be a substring of the full field: - - $ kustomize config set create dir/ app-image-tag v1.0.01 --type "string" --field "image" - - image: gcr.io/example/app:v1.0.1 # {"substitutions":[{"name":"app-image-tag","marker":"[MARKER]","value":"v1.0.1"}]} - - -A single field value may have multiple substitutions applied to it: - - name: PREFIX-app-SUFFIX # {"substitutions":[{"name":"prefix","marker":"PREFIX-"},{"name":"suffix","marker":"-SUFFIX"}]} - -#### Substitution Format - -Substitutions are defined as json encoded FieldMeta comments on fields. - -FieldMeta Schema read by ` + "`" + `sub` + "`" + `: - - { - "title": "FieldMeta", - "type": "object", - "properties": { - "substitutions": { - "type": "array", - "description": "Possible substitutions that may be performed against this field.", - "items": { - "type": "object", - "properties": { - "name": "Name of the substitution.", - "marker": "Marker for the value to be substituted.", - "value": "Current substituted value" - } - } - }, - "type": { - "type": "string", - "description": "The value type. Defaults to string." - "enum": ["string", "int", "float", "bool"] - }, - "description": { - "type": "string", - "description": "A description of the field's current value. Optional." - }, - "setBy": { - "type": "string", - "description": "The current owner of the field. Optional." - }, - } - } -` -var SubsetExamples = ` - # set a substitution for port fields matching "8080" - kustomize config sub create dir/ port 8080 --type "int" --field port \ - --description "default port used by the app" - - # set a substitution for port fields matching "8080", using "0000" as a marker. - kustomize config sub dir/ port 8080 --marker "0000" --type "int" \ - --field port --description "default port used by the app" - - # substitute a substring of a field rather than the full field -- e.g. only the - # image tag, not the full image - kustomize config sub dir/ app-image-tag v1.0.1 --type "string" --substring \ - --field port --description "current stable release"` + name: test-app2 # {"description":"test environment","type":"string","x-kustomize":{"setBy":"dev","partialFieldSetters":[{"name":"name-prefix","value":"test"}]}} + ...` var TreeShort = `[Alpha] Display Resource structure from a directory or stdin.` var TreeLong = ` diff --git a/cmd/kubectl/go.sum b/cmd/kubectl/go.sum index dac361f72..5fa7dd62e 100644 --- a/cmd/kubectl/go.sum +++ b/cmd/kubectl/go.sum @@ -85,6 +85,7 @@ github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL9 github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-openapi/swag v0.19.2 h1:jvO6bCMBEilGwMfHhrd61zIID4oIFdwb76V17SM88dE= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= diff --git a/cmd/resource/go.sum b/cmd/resource/go.sum index 20b8392c8..40b7ef114 100644 --- a/cmd/resource/go.sum +++ b/cmd/resource/go.sum @@ -74,6 +74,7 @@ github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+ github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= @@ -88,6 +89,7 @@ github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nA github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= @@ -95,6 +97,7 @@ github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dp github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= @@ -167,6 +170,7 @@ github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -280,6 +284,7 @@ golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190909003024-a7b16738d86b h1:XfVGCX+0T4WOStkaOsJRllbsiImhB2jgVBGc9L0lPGc= golang.org/x/net v0.0.0-20190909003024-a7b16738d86b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI= diff --git a/kyaml/fieldmeta/fieldmeta.go b/kyaml/fieldmeta/fieldmeta.go index 9447c80af..3a9742654 100644 --- a/kyaml/fieldmeta/fieldmeta.go +++ b/kyaml/fieldmeta/fieldmeta.go @@ -4,37 +4,35 @@ package fieldmeta import ( - "bytes" "encoding/json" "strconv" "strings" + "github.com/go-openapi/spec" "sigs.k8s.io/kustomize/kyaml/errors" "sigs.k8s.io/kustomize/kyaml/yaml" ) // FieldMeta contains metadata that may be attached to fields as comments type FieldMeta struct { - // Substitutions are substitutions that may be performed against this field - Substitutions []Substitution `yaml:"substitutions,omitempty" json:"substitutions,omitempty"` - // OwnedBy records the owner of this field - OwnedBy string `yaml:"setBy,omitempty" json:"setBy,omitempty"` - // DefaultedBy records that this field was default, but may be changed by other owners - DefaultedBy string `yaml:"defaultedBy,omitempty" json:"defaultedBy,omitempty"` - // Description is a description of the current field value, e.g. why it was set - Description string `yaml:"description,omitempty" json:"description,omitempty"` - // Type is the type of the field value - Type FieldValueType `yaml:"type,omitempty" json:"type,omitempty"` + Schema spec.Schema + + Extensions XKustomize } -// Substitution defines a substitution that may be performed against the field -type Substitution struct { - // Name is the name of the substitution and read by tools - Name string `yaml:"name,omitempty" json:"name,omitempty"` - // Marker is the marker used for replacement - Marker string `yaml:"marker,omitempty" json:"marker,omitempty"` - // Value is the current value that has been substituted for the Marker - Value string `yaml:"value,omitempty" json:"value,omitempty"` +type XKustomize struct { + SetBy string `yaml:"setBy,omitempty" json:"setBy,omitempty"` + PartialFieldSetters []PartialFieldSetter `yaml:"partialFieldSetters" json:"partialFieldSetters"` +} + +// PartialFieldSetter defines how to set part of a field rather than the full field +// value. e.g. the tag part of an image field +type PartialFieldSetter struct { + // Name is the name of this setter. + Name string `yaml:"name" json:"name"` + + // Value is the current value that has been set. + Value string `yaml:"value" json:"value"` } // Read reads the FieldMeta from a node @@ -43,16 +41,30 @@ func (fm *FieldMeta) Read(n *yaml.RNode) error { v := strings.TrimLeft(n.YNode().LineComment, "#") // if it doesn't Unmarshal that is fine, it means there is no metadata // other comments are valid, they just don't parse - d := yaml.NewDecoder(bytes.NewBuffer([]byte(v))) - d.KnownFields(false) - _ = d.Decode(fm) + + // TODO: consider most sophisticated parsing techniques similar to what is used + // for go struct tags. + if err := fm.Schema.UnmarshalJSON([]byte(v)); err != nil { + // note: don't return an error if the comment isn't a fieldmeta struct + return nil + } + fe := fm.Schema.VendorExtensible.Extensions["x-kustomize"] + if fe == nil { + return nil + } + b, err := json.Marshal(fe) + if err != nil { + return err + } + return json.Unmarshal(b, &fm.Extensions) } return nil } // Write writes the FieldMeta to a node func (fm *FieldMeta) Write(n *yaml.RNode) error { - b, err := json.Marshal(fm) + fm.Schema.VendorExtensible.AddExtension("x-kustomize", fm.Extensions) + b, err := json.Marshal(fm.Schema) if err != nil { return err } @@ -67,11 +79,9 @@ const ( // String defines a string flag String FieldValueType = "string" // Bool defines a bool flag - Bool = "bool" - // Float defines a float flag - Float = "float" + Bool = "boolean" // Int defines an int flag - Int = "int" + Int = "integer" ) func (it FieldValueType) String() string { @@ -91,10 +101,6 @@ func (it FieldValueType) Validate(value string) error { if _, err := strconv.ParseBool(value); err != nil { return errors.WrapPrefixf(err, "value must be a bool") } - case Float: - if _, err := strconv.ParseFloat(value, 64); err != nil { - return errors.WrapPrefixf(err, "value must be a float") - } } return nil } @@ -107,8 +113,6 @@ func (it FieldValueType) Tag() string { return "!!bool" case Int: return "!!int" - case Float: - return "!!float" } return "" } @@ -127,11 +131,6 @@ func (it FieldValueType) TagForValue(value string) string { return "" } return "!!int" - case Float: - if _, err := strconv.ParseFloat(string(it), 64); err != nil { - return "" - } - return "!!float" } return "" } diff --git a/kyaml/go.mod b/kyaml/go.mod index 7bb60a053..a19a394de 100644 --- a/kyaml/go.mod +++ b/kyaml/go.mod @@ -5,10 +5,8 @@ go 1.12 require ( github.com/davecgh/go-spew v1.1.1 github.com/go-errors/errors v1.0.1 - github.com/kr/pretty v0.1.0 // indirect + github.com/go-openapi/spec v0.19.5 github.com/stretchr/testify v1.4.0 github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca - gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect - gopkg.in/yaml.v2 v2.2.4 // indirect gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d ) diff --git a/kyaml/go.sum b/kyaml/go.sum index 7e5637da6..bec7cb8b0 100644 --- a/kyaml/go.sum +++ b/kyaml/go.sum @@ -1,25 +1,50 @@ +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/spec v0.19.5 h1:Xm0Ao53uqnk9QE/LlYV5DEU09UAgpliA85QoT9LzqPw= +github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI= github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/kyaml/set/addyaml.go b/kyaml/set/addyaml.go deleted file mode 100644 index 3255c2560..000000000 --- a/kyaml/set/addyaml.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package set - -import ( - "strings" - - "sigs.k8s.io/kustomize/kyaml/errors" - "sigs.k8s.io/kustomize/kyaml/fieldmeta" - "sigs.k8s.io/kustomize/kyaml/yaml" -) - -var _ yaml.Filter = &Marker{} - -// substituteResource substitutes a Marker value on a field -type Marker struct { - // Path is the path of the field to add the substitution for - Field string - - // Substitution is the substitution to add - Substitution fieldmeta.Substitution - - // PartialMatch if true will match if the Substitution value is a substring of the current - // value. - PartialMatch bool - - Description string - OwnedBy string - Type string - - // currentFieldName is the name of the current field being processed - currentFieldName string -} - -// Filter performs the substitutions for a single object -func (m *Marker) Filter(object *yaml.RNode) (*yaml.RNode, error) { - switch object.YNode().Kind { - case yaml.DocumentNode: - return m.Filter(yaml.NewRNode(object.YNode().Content[0])) - case yaml.MappingNode: - return object, object.VisitFields(func(node *yaml.MapNode) error { - // set the current field name - n := m.currentFieldName - defer func() { m.currentFieldName = n }() - m.currentFieldName = node.Key.YNode().Value - _, err := m.Filter(node.Value) - return err - }) - case yaml.SequenceNode: - return object, object.VisitElements(func(node *yaml.RNode) error { - _, err := m.Filter(node) - return err - }) - case yaml.ScalarNode: - if m.currentFieldName != m.Field { - return object, nil - } - if err := m.createSub(object); err != nil { - return nil, err - } - return object, nil - default: - return object, nil - } -} - -func (m *Marker) createSub(field *yaml.RNode) error { - // doesn't match the supplied value - if field.YNode().Value != m.Substitution.Value { - if !m.PartialMatch || !strings.Contains(field.YNode().Value, m.Substitution.Value) { - return nil - } - } - - fm := fieldmeta.FieldMeta{} - if err := fm.Read(field); err != nil { - return errors.Wrap(err) - } - fm.OwnedBy = m.OwnedBy - fm.Description = m.Description - fm.Type = fieldmeta.FieldValueType(m.Type) - if m.Substitution.Marker == "" { - m.Substitution.Marker = "[MARKER]" - } - - found := false - for i := range fm.Substitutions { - s := fm.Substitutions[i] - if s.Name == m.Substitution.Name { - // update the substitution if we find it - found = true - fm.Substitutions[i] = m.Substitution - break - } - } - if !found { - // add the substitution if it wasn't found - fm.Substitutions = append(fm.Substitutions, m.Substitution) - } - if err := fm.Write(field); err != nil { - return errors.Wrap(err) - } - return nil -} diff --git a/kyaml/set/dokio.go b/kyaml/set/dokio.go deleted file mode 100644 index cf982b619..000000000 --- a/kyaml/set/dokio.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package set - -import ( - "sigs.k8s.io/kustomize/kyaml/kio" - "sigs.k8s.io/kustomize/kyaml/yaml" -) - -var _ kio.Filter = &PerformSubstitutions{} - -// Sub performs substitutions -type PerformSubstitutions struct { - // Name is the name of the substitution to perform - Name string - - // NewValue is the substitution value - NewValue string - - // Override if set to true will re-substitute already fields with a new value - Override bool - - // Revert if set to true will substitute fields back to the marker value - Revert bool - - // Description, if set will annotate the field with a description. - Description string - - // OwnedBy, if set will annotate the field with an owner. - OwnedBy string - - // Count is the number of substitutions performed by Filter. - Count int -} - -func (s *PerformSubstitutions) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) { - for i := range input { - p := &performSubstitutions{ - Name: s.Name, - Override: s.Override, - Revert: s.Revert, - NewValue: s.NewValue, - OwnedBy: s.OwnedBy, - Description: s.Description, - } - if err := input[i].PipeE(p); err != nil { - return nil, err - } - s.Count += p.Count - } - return input, nil -} diff --git a/kyaml/set/doyaml.go b/kyaml/set/doyaml.go deleted file mode 100644 index 4b78edf18..000000000 --- a/kyaml/set/doyaml.go +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2019 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package sub substitutes strings in fields -package set - -import ( - "strings" - - "sigs.k8s.io/kustomize/kyaml/fieldmeta" - "sigs.k8s.io/kustomize/kyaml/yaml" -) - -var _ yaml.Filter = &performSubstitutions{} - -// substituteResource substitutes a Marker value on a field -type performSubstitutions struct { - // Name of the substitution to perform. - Name string - - // Override if set to true will replace previously substituted values - Override bool - - // Revert if set to true will undo previously substituted values - Revert bool - - // NewValue is the new value to set. Mutually exclusive with Revert. - NewValue string - - // Description, if set will annotate the field with a description. - Description string - - // OwnedBy, if set will annotate the field with an owner. - OwnedBy string - - // Count will be incremented for each substituted value. - Count int -} - -// Filter performs the substitutions for a single object -func (fs *performSubstitutions) Filter(object *yaml.RNode) (*yaml.RNode, error) { - switch object.YNode().Kind { - case yaml.DocumentNode: - return fs.Filter(yaml.NewRNode(object.YNode().Content[0])) - case yaml.MappingNode: - return object, object.VisitFields(func(node *yaml.MapNode) error { - _, err := fs.Filter(node.Value) - return err - }) - case yaml.SequenceNode: - return object, object.VisitElements(func(node *yaml.RNode) error { - _, err := fs.Filter(node) - return err - }) - case yaml.ScalarNode: - s, f, err := fs.findSub(object) - if err != nil { - return nil, err - } - if s == nil { - return object, nil - } - return object, fs.substitute(object, s, f) - default: - return object, nil - } -} - -// findSub finds the substitution matching the name if one exists -func (fs *performSubstitutions) findSub(field *yaml.RNode) ( - *fieldmeta.Substitution, *fieldmeta.FieldMeta, error) { - // check if there are any substitutions for this field - var fm = &fieldmeta.FieldMeta{} - if err := fm.Read(field); err != nil { - return nil, nil, err - } - if fs.OwnedBy != "" { - fm.OwnedBy = fs.OwnedBy - } - if fs.Description != "" { - fm.Description = fs.Description - } - - // check if there is a matching substitution - for i := range fm.Substitutions { - if fm.Substitutions[i].Name == fs.Name { - // validate the value if we are not reverting to the marker. - // markers are allowed to be invalid. - // only validate if there is a substitution matching the name - if !fs.Revert { - if err := fm.Type.Validate(fs.NewValue); err != nil { - return nil, nil, err - } - } - return &fm.Substitutions[i], fm, nil - } - } - return nil, nil, nil -} - -// substitute performs the substitution for the given field, substitution, and metadata -func (fs *performSubstitutions) substitute( - field *yaml.RNode, s *fieldmeta.Substitution, f *fieldmeta.FieldMeta) error { - // undo or override previous substitutions by substituting the marker back - // NOTE: check if s.Value != "" so we never try to substitute the empty string back - if (fs.Revert || fs.Override) && s.Value != "" { - // revert to the marker value - if strings.Contains(field.YNode().Value, s.Value) { - // revert the substitution - field.YNode().Value = strings.ReplaceAll(field.YNode().Value, s.Value, s.Marker) - // only use the tag matching the type if the marker parses to that type - field.YNode().Tag = f.Type.TagForValue(s.Marker) - // record that the config has been modified - } - } - if fs.Revert { - fs.Count++ - s.Value = "" // value has been cleared and replaced with marker - if err := f.Write(field); err != nil { - return err - } - return nil - } - - if s.Value == fs.NewValue || !strings.Contains(field.YNode().Value, s.Marker) { - // no substitutions necessary -- already substituted or doesn't have the marker - return nil - } - - // replace the marker with the new value - field.YNode().Value = strings.ReplaceAll(field.YNode().Value, s.Marker, fs.NewValue) - // be sure to set the tag so the yaml doesn't incorrectly quote ints, bools or floats - field.YNode().Tag = f.Type.Tag() - field.YNode().Style = 0 - // record that the config has been modified - fs.Count++ - - // update the comment on the field - s.Value = fs.NewValue - if err := f.Write(field); err != nil { - return err - } - return nil -} diff --git a/kyaml/set/lookupkio.go b/kyaml/set/lookupkio.go deleted file mode 100644 index 05ac4b65a..000000000 --- a/kyaml/set/lookupkio.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package set - -import ( - "sort" - - "sigs.k8s.io/kustomize/kyaml/kio" - "sigs.k8s.io/kustomize/kyaml/yaml" -) - -var _ kio.Filter = &LookupSubstitutions{} - -// Sub performs substitutions -type LookupSubstitutions struct { - // Name is the name of the substitution to match. If unspecified, all substitutions will - // be matched. - Name string - - // SubstitutionCounts are the aggregate substitutions matched. - SubstitutionCounts []FieldSubstitutionCount -} - -type FieldSubstitutionCount struct { - // Count is the number of substitutions possible to perform - Count int - - // CountComplete is the number of substitutions that have already been performed - // independent of this object. - CountComplete int - - // FieldSubstitution is the substitution found - FieldSubstitution -} - -func (l *LookupSubstitutions) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) { - subs := map[string]*FieldSubstitutionCount{} - for i := range input { - // lookup substitutions for this object - ls := &lookupSubstitutions{Name: l.Name} - if err := input[i].PipeE(ls); err != nil { - return nil, err - } - - // aggregate counts for each substitution - for j := range ls.Substitutions { - sub := ls.Substitutions[j] - curr, found := subs[sub.Name] - if !found { - curr = &FieldSubstitutionCount{FieldSubstitution: sub} - subs[sub.Name] = curr - } - curr.Count++ - if sub.CurrentValue != "" { - curr.CountComplete++ - } - } - } - - // pull out and sort the results - for _, v := range subs { - l.SubstitutionCounts = append(l.SubstitutionCounts, *v) - } - sort.Slice(l.SubstitutionCounts, func(i, j int) bool { - return l.SubstitutionCounts[i].Name < l.SubstitutionCounts[j].Name - }) - return input, nil -} diff --git a/kyaml/set/lookupyaml.go b/kyaml/set/lookupyaml.go deleted file mode 100644 index 7c26acdbb..000000000 --- a/kyaml/set/lookupyaml.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package set - -import ( - "sigs.k8s.io/kustomize/kyaml/fieldmeta" - "sigs.k8s.io/kustomize/kyaml/yaml" -) - -var _ yaml.Filter = &lookupSubstitutions{} - -// substituteResource substitutes a Marker value on a field -type lookupSubstitutions struct { - // Name of the substitution to lookup. If unspecified lookup all substitutions. - Name string - - // FieldSubstitution is the list of substitutions that were found - Substitutions []FieldSubstitution -} - -func (ls *lookupSubstitutions) Filter(object *yaml.RNode) (*yaml.RNode, error) { - switch object.YNode().Kind { - case yaml.DocumentNode: - return ls.Filter(yaml.NewRNode(object.YNode().Content[0])) - case yaml.MappingNode: - return object, object.VisitFields(func(node *yaml.MapNode) error { - _, err := ls.Filter(node.Value) - return err - }) - case yaml.SequenceNode: - return object, object.VisitElements(func(node *yaml.RNode) error { - _, err := ls.Filter(node) - return err - }) - case yaml.ScalarNode: - return object, ls.lookup(object) - default: - return object, nil - } -} - -// lookup finds any substitutions for this field -func (ls *lookupSubstitutions) lookup(field *yaml.RNode) error { - // check if there is a substitution for this field - var fm = &fieldmeta.FieldMeta{} - if err := fm.Read(field); err != nil { - return err - } - - for i := range fm.Substitutions { - s := fm.Substitutions[i] - if ls.Name == "" || ls.Name == s.Name { - ls.Substitutions = append(ls.Substitutions, FieldSubstitution{ - Name: s.Name, - CurrentValue: s.Value, - Description: fm.Description, - Marker: s.Marker, - Type: fm.Type, - OwnedBy: fm.OwnedBy, - }) - } - } - return nil -} diff --git a/kyaml/set/types.go b/kyaml/set/types.go deleted file mode 100644 index 5b8557398..000000000 --- a/kyaml/set/types.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package set - -import ( - "sigs.k8s.io/kustomize/kyaml/fieldmeta" -) - -// FieldSubstitution is a possible field substitution read from a field -type FieldSubstitution struct { - // Name is the name of the substitution - Name string - - // Description is a description of the fields current value - Description string - - // Value is the current substituted value for the field. - CurrentValue string - - // Type is the type of the substitution - Type fieldmeta.FieldValueType - - // Marker is the marker used - Marker string - - // OwnedBy, if set will annotate the field with an owner. - OwnedBy string -} diff --git a/kyaml/set/addkio.go b/kyaml/setters/addkio.go similarity index 60% rename from kyaml/set/addkio.go rename to kyaml/setters/addkio.go index 2ec66213a..c77ba2f3c 100644 --- a/kyaml/set/addkio.go +++ b/kyaml/setters/addkio.go @@ -1,25 +1,25 @@ // Copyright 2019 The Kubernetes Authors. // SPDX-License-Identifier: Apache-2.0 -package set +package setters import ( "sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/yaml" ) -var _ kio.Filter = &SetSubstitutionMarker{} +var _ kio.Filter = &CreateSetter{} -// Sub performs substitutions -type SetSubstitutionMarker struct { - // Marker is the marker to set - Marker Marker +// CreateSetter creates a custom setter as an OpenAPI property through a comment +type CreateSetter struct { + // customFieldSetter is the marker to set + SetPartialField customFieldSetter // ResourceMeta defines the Resource to set the marker on ResourceMeta yaml.ResourceMeta } -func (s *SetSubstitutionMarker) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) { +func (s *CreateSetter) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) { for i := range input { m, err := input[i].GetMeta() if err != nil { @@ -31,7 +31,7 @@ func (s *SetSubstitutionMarker) Filter(input []*yaml.RNode) ([]*yaml.RNode, erro if s.ResourceMeta.Kind != "" && m.Kind != s.ResourceMeta.Kind { continue } - if err := input[i].PipeE(&s.Marker); err != nil { + if err := input[i].PipeE(&s.SetPartialField); err != nil { return nil, err } } diff --git a/kyaml/setters/addyaml.go b/kyaml/setters/addyaml.go new file mode 100644 index 000000000..22aec3696 --- /dev/null +++ b/kyaml/setters/addyaml.go @@ -0,0 +1,104 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package setters + +import ( + "strings" + + "sigs.k8s.io/kustomize/kyaml/errors" + "sigs.k8s.io/kustomize/kyaml/fieldmeta" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +var _ yaml.Filter = &customFieldSetter{} + +// customFieldSetter creates a new custom field setter +type customFieldSetter struct { + // Path is the path of the field to add the setter for + Field string + + // Setter is the setter to add + Setter fieldmeta.PartialFieldSetter + + // Description is the description to add to the OpenAPI + Description string + + // SetBy is the setBy to add to the OpenAPI extension + SetBy string + + Type string + + // currentFieldName is the name of the current field being processed + currentFieldName string +} + +// Filter performs the setter for a single object +func (m *customFieldSetter) Filter(object *yaml.RNode) (*yaml.RNode, error) { + switch object.YNode().Kind { + case yaml.DocumentNode: + return m.Filter(yaml.NewRNode(object.YNode().Content[0])) + case yaml.MappingNode: + return object, object.VisitFields(func(node *yaml.MapNode) error { + // record the current field name, resetting it back to its original value + // when done + n := m.currentFieldName + defer func() { m.currentFieldName = n }() + m.currentFieldName = node.Key.YNode().Value + return node.Value.PipeE(m) + }) + case yaml.SequenceNode: + return object, object.VisitElements(func(node *yaml.RNode) error { + return node.PipeE(m) + }) + case yaml.ScalarNode: + // only create the setter for fields with the given name + if m.currentFieldName != m.Field { + return object, nil + } + if err := m.create(object); err != nil { + return nil, err + } + return object, nil + default: + return object, nil + } +} + +func (m *customFieldSetter) create(field *yaml.RNode) error { + // doesn't match the supplied value + if !strings.Contains(field.YNode().Value, m.Setter.Value) { + return nil + } + + fm := fieldmeta.FieldMeta{} + if err := fm.Read(field); err != nil { + return errors.Wrap(err) + } + + if m.Description != "" { + fm.Schema.Description = m.Description + } + + fm.Extensions.SetBy = m.SetBy + fm.Schema.Type = []string{m.Type} + + found := false + for i := range fm.Extensions.PartialFieldSetters { + s := fm.Extensions.PartialFieldSetters[i] + if s.Name == m.Setter.Name { + // update the setter if we find it + found = true + fm.Extensions.PartialFieldSetters[i] = m.Setter + break + } + } + if !found { + // add the setter if it wasn't found + fm.Extensions.PartialFieldSetters = append(fm.Extensions.PartialFieldSetters, m.Setter) + } + if err := fm.Write(field); err != nil { + return errors.Wrap(err) + } + return nil +} diff --git a/kyaml/setters/dokio.go b/kyaml/setters/dokio.go new file mode 100644 index 000000000..182a73122 --- /dev/null +++ b/kyaml/setters/dokio.go @@ -0,0 +1,44 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package setters + +import ( + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +var _ kio.Filter = &PerformSetters{} + +// PerformSetters sets field values +type PerformSetters struct { + // Name is the name of the setter to perform + Name string + + // Value is the value to set + Value string + + // Description, if set will annotate the field with a description. + Description string + + // SetBy, if set will annotate the field with who set it. + SetBy string + + // Count is set by Filter and is the number of fields modified. + Count int +} + +func (s *PerformSetters) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) { + for i := range input { + p := &partialFieldSetter{ + Name: s.Name, + Value: s.Value, + Description: s.Description, + } + if err := input[i].PipeE(p); err != nil { + return nil, err + } + s.Count += p.Count + } + return input, nil +} diff --git a/kyaml/setters/doyaml.go b/kyaml/setters/doyaml.go new file mode 100644 index 000000000..69d64acc2 --- /dev/null +++ b/kyaml/setters/doyaml.go @@ -0,0 +1,120 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Package sub substitutes strings in fields +package setters + +import ( + "strings" + + "sigs.k8s.io/kustomize/kyaml/fieldmeta" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +var _ yaml.Filter = &partialFieldSetter{} + +// partialFieldSetter sets part of a field value. +type partialFieldSetter struct { + // Name is the name of the setter to perform. + Name string + + // Value is the value to set. + Value string + + // Description, if specified will set 'description' for the field. Optional. + Description string + + // SetBy, if specified will set 'setBy' for the field. Optional. + SetBy string + + // Count is incremented by Filter for each field that is set. + Count int +} + +// Filter implements yaml.Filter +func (fs *partialFieldSetter) Filter(object *yaml.RNode) (*yaml.RNode, error) { + switch object.YNode().Kind { + case yaml.DocumentNode: + // Document is the root of the object and always contains 1 node + return fs.Filter(yaml.NewRNode(object.YNode().Content[0])) + case yaml.MappingNode: + return object, object.VisitFields(func(node *yaml.MapNode) error { + // Traverse each field value + return node.Value.PipeE(fs) + }) + case yaml.SequenceNode: + return object, object.VisitElements(func(node *yaml.RNode) error { + // Traverse each list element + return node.PipeE(fs) + }) + case yaml.ScalarNode: + // Check if there is a setter matching the name + s, f, err := fs.findPartialSetter(object) + if err != nil { + return nil, err + } + if s == nil { + // no matching setter + return object, nil + } + // set the field value + return object, fs.set(object, s, f) + default: + return object, nil + } +} + +// findPartialSetter finds the setter matching the name if one exists +func (fs *partialFieldSetter) findPartialSetter(field *yaml.RNode) ( + *fieldmeta.PartialFieldSetter, *fieldmeta.FieldMeta, error) { + // check if there are any substitutions for this field + var fm = &fieldmeta.FieldMeta{} + if err := fm.Read(field); err != nil { + return nil, nil, err + } + if fs.SetBy != "" { + fm.Extensions.SetBy = fs.SetBy + } + if fs.Description != "" { + fm.Schema.Description = fs.Description + } + + // check if there is a matching substitution + for i := range fm.Extensions.PartialFieldSetters { + if fm.Extensions.PartialFieldSetters[i].Name == fs.Name { + return &fm.Extensions.PartialFieldSetters[i], fm, nil + } + } + return nil, nil, nil +} + +// set performs the substitution for the given field, substitution, and metadata +func (fs *partialFieldSetter) set( + field *yaml.RNode, s *fieldmeta.PartialFieldSetter, f *fieldmeta.FieldMeta) error { + if s.Value == fs.Value || !strings.Contains(field.YNode().Value, s.Value) { + // no substitutions necessary -- already substituted or doesn't have the set value + // which acts as a marker + return nil + } + + // record that the config has been modified + fs.Count++ + + // replace the current value with the new value + field.YNode().Value = strings.ReplaceAll(field.YNode().Value, s.Value, fs.Value) + + // be sure to set the tag to the matching type so the yaml doesn't incorrectly quote + //integers or booleans as strings + fType := fieldmeta.FieldValueType(f.Schema.Type[0]) + if err := fType.Validate(field.YNode().Value); err != nil { + return err + } + field.YNode().Tag = fType.Tag() + + // update the comment on the field + s.Value = fs.Value + if err := f.Write(field); err != nil { + return err + } + return nil +} diff --git a/kyaml/setters/lookupkio.go b/kyaml/setters/lookupkio.go new file mode 100644 index 000000000..541c53686 --- /dev/null +++ b/kyaml/setters/lookupkio.go @@ -0,0 +1,65 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package setters + +import ( + "sort" + + "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +var _ kio.Filter = &LookupSetters{} + +// LookupSetters identifies setters for a collection of Resources +type LookupSetters struct { + // Name is the name of the setter to match. Optional. + Name string + + // SetterCounts is populated by Filter and contains the count of fields matching each setter. + SetterCounts []setterCount +} + +// setterCount records the identified setters and number of fields matching those setters +type setterCount struct { + // Count is the number of substitutions possible to perform + Count int + + // setter is the substitution found + setter +} + +// Filter implements kio.Filter +func (l *LookupSetters) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) { + setters := map[string]*setterCount{} + + for i := range input { + // lookup substitutions for this object + ls := &lookupSetters{Name: l.Name} + if err := input[i].PipeE(ls); err != nil { + return nil, err + } + + // aggregate counts for each setter by name. takes the description and value from + // the first setter for each name encountered. + for j := range ls.Setters { + setter := ls.Setters[j] + curr, found := setters[setter.Name] + if !found { + curr = &setterCount{setter: setter} + setters[setter.Name] = curr + } + curr.Count++ + } + } + + // pull out and sort the results by setter name + for _, v := range setters { + l.SetterCounts = append(l.SetterCounts, *v) + } + sort.Slice(l.SetterCounts, func(i, j int) bool { + return l.SetterCounts[i].Name < l.SetterCounts[j].Name + }) + return input, nil +} diff --git a/kyaml/setters/lookupyaml.go b/kyaml/setters/lookupyaml.go new file mode 100644 index 000000000..425b58a62 --- /dev/null +++ b/kyaml/setters/lookupyaml.go @@ -0,0 +1,70 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package setters + +import ( + "sigs.k8s.io/kustomize/kyaml/fieldmeta" + "sigs.k8s.io/kustomize/kyaml/yaml" +) + +var _ yaml.Filter = &lookupSetters{} + +// lookupSetters looks up setters for a Resource +type lookupSetters struct { + // Name of the setter to lookup. Optional + Name string + + // Setters is a list of setters that were found + Setters []setter +} + +type setter struct { + fieldmeta.PartialFieldSetter + Description string + Type string + SetBy string +} + +func (ls *lookupSetters) Filter(object *yaml.RNode) (*yaml.RNode, error) { + switch object.YNode().Kind { + case yaml.DocumentNode: + // skip the document node + return ls.Filter(yaml.NewRNode(object.YNode().Content[0])) + case yaml.MappingNode: + return object, object.VisitFields(func(node *yaml.MapNode) error { + return node.Value.PipeE(ls) + }) + case yaml.SequenceNode: + return object, object.VisitElements(func(node *yaml.RNode) error { + return node.PipeE(ls) + }) + case yaml.ScalarNode: + return object, ls.lookup(object) + default: + return object, nil + } +} + +// lookup finds any setters for a field +func (ls *lookupSetters) lookup(field *yaml.RNode) error { + // check if there is a substitution for this field + var fm = &fieldmeta.FieldMeta{} + if err := fm.Read(field); err != nil { + return err + } + + for i := range fm.Extensions.PartialFieldSetters { + if ls.Name != "" && ls.Name != fm.Extensions.PartialFieldSetters[i].Name { + // skip this setter + continue + } + ls.Setters = append(ls.Setters, setter{ + PartialFieldSetter: fm.Extensions.PartialFieldSetters[i], + Description: fm.Schema.Description, + Type: fm.Schema.Type[0], + SetBy: fm.Extensions.SetBy, + }) + } + return nil +}