diff --git a/docs/design-docs/08-package-variant.md b/docs/design-docs/08-package-variant.md index 684b7a4dc..1ebd52909 100644 --- a/docs/design-docs/08-package-variant.md +++ b/docs/design-docs/08-package-variant.md @@ -916,91 +916,117 @@ template API is shown below. ```go type PackageVariantTemplate struct { - // Downstream allows overriding the default downstream package and repository name - // +optional - Downstream *DownstreamTemplate `json:"downstream,omitempty"` - - // AdoptionPolicy allows overriding the PackageVariant adoption policy - // +optional - AdoptionPolicy *pkgvarapi.AdoptionPolicy `json:"adoptionPolicy,omitempty"` - - // DeletionPolicy allows overriding the PackageVariant deletion policy - // +optional - DeletionPolicy *pkgvarapi.DeletionPolicy `json:"deletionPolicy,omitempty"` - - // Labels allows specifying the spec.Labels field of the generated PackageVariant - // +optional - Labels map[string]string `json:"labels,omitempty"` - - // LabelsExprs allows specifying the spec.Labels field of the generated PackageVariant - // using CEL to dynamically create the keys and values. Entries in this field take precedent over - // those with the same keys that are present in Labels. - // +optional - LabelExprs []MapExpr `json:"labelExprs,omitemtpy"` - - // Annotations allows specifying the spec.Annotations field of the generated PackageVariant - // +optional - Annotations map[string]string `json:"annotations,omitempty"` - - // AnnotationsExprs allows specifying the spec.Annotations field of the generated PackageVariant - // using CEL to dynamically create the keys and values. Entries in this field take precedent over - // those with the same keys that are present in Annotations. - // +optional - AnnotationExprs []MapExpr `json:"annotationExprs,omitempty"` - - // PackageContext allows specifying the spec.PackageContext field of the generated PackageVariant - // +optional - PackageContext *PackageContextTemplate `json:"packageContext,omitempty"` - - // Pipeline allows specifying the spec.Pipeline field of the generated PackageVariant - // +optional - Pipeline *kptfilev1.Pipeline `json:"pipeline,omitempty"` - - // Injectors allows specifying the spec.Injectors field of the generated PackageVariant - // +optional - Injectors []InjectionSelectorTemplate `json:"injectors,omitempty"` + // Downstream allows overriding the default downstream package and repository name + // +optional + Downstream *DownstreamTemplate `json:"downstream,omitempty"` + + // AdoptionPolicy allows overriding the PackageVariant adoption policy + // +optional + AdoptionPolicy *pkgvarapi.AdoptionPolicy `json:"adoptionPolicy,omitempty"` + + // DeletionPolicy allows overriding the PackageVariant deletion policy + // +optional + DeletionPolicy *pkgvarapi.DeletionPolicy `json:"deletionPolicy,omitempty"` + + // Labels allows specifying the spec.Labels field of the generated PackageVariant + // +optional + Labels map[string]string `json:"labels,omitempty"` + + // LabelsExprs allows specifying the spec.Labels field of the generated PackageVariant + // using CEL to dynamically create the keys and values. Entries in this field take precedent over + // those with the same keys that are present in Labels. + // +optional + LabelExprs []MapExpr `json:"labelExprs,omitempty"` + + // Annotations allows specifying the spec.Annotations field of the generated PackageVariant + // +optional + Annotations map[string]string `json:"annotations,omitempty"` + + // AnnotationsExprs allows specifying the spec.Annotations field of the generated PackageVariant + // using CEL to dynamically create the keys and values. Entries in this field take precedent over + // those with the same keys that are present in Annotations. + // +optional + AnnotationExprs []MapExpr `json:"annotationExprs,omitempty"` + + // PackageContext allows specifying the spec.PackageContext field of the generated PackageVariant + // +optional + PackageContext *PackageContextTemplate `json:"packageContext,omitempty"` + + // Pipeline allows specifying the spec.Pipeline field of the generated PackageVariant + // +optional + Pipeline *PipelineTemplate `json:"pipeline,omitempty"` + + // Injectors allows specifying the spec.Injectors field of the generated PackageVariant + // +optional + Injectors []InjectionSelectorTemplate `json:"injectors,omitempty"` } // DownstreamTemplate is used to calculate the downstream field of the resulting // package variants. Only one of Repo and RepoExpr may be specified; // similarly only one of Package and PackageExpr may be specified. type DownstreamTemplate struct { - Repo *string `json:"repo,omitempty"` - Package *string `json:"package,omitempty"` - RepoExpr *string `json:"repoExpr,omitempty"` - PackageExpr *string `json:"packageExpr,omitempty"` + Repo *string `json:"repo,omitempty"` + Package *string `json:"package,omitempty"` + RepoExpr *string `json:"repoExpr,omitempty"` + PackageExpr *string `json:"packageExpr,omitempty"` } // PackageContextTemplate is used to calculate the packageContext field of the // resulting package variants. The plain fields and Exprs fields will be // merged, with the Exprs fields taking precedence. type PackageContextTemplate struct { - Data map[string]string `json:"data,omitempty"` - RemoveKeys []string `json:"removeKeys,omitempty"` - DataExprs []MapExpr `json:"dataExprs,omitempty"` - RemoveKeyExprs []string `json:"removeKeyExprs,omitempty"` + Data map[string]string `json:"data,omitempty"` + RemoveKeys []string `json:"removeKeys,omitempty"` + DataExprs []MapExpr `json:"dataExprs,omitempty"` + RemoveKeyExprs []string `json:"removeKeyExprs,omitempty"` } // InjectionSelectorTemplate is used to calculate the injectors field of the -// resulting package variants. Only one of the Name and NameExpr fields may be -// specified. +// resulting package variants. Exactly one of the Name and NameExpr fields must +// be specified. The other fields are optional. type InjectionSelectorTemplate struct { - Group *string `json:"group,omitempty"` - Version *string `json:"version,omitempty"` - Kind *string `json:"kind,omitempty"` - Name *string `json:"name,omitempty"` + Group *string `json:"group,omitempty"` + Version *string `json:"version,omitempty"` + Kind *string `json:"kind,omitempty"` + Name *string `json:"name,omitempty"` - NameExpr *string `json:"nameExpr,omitempty"` + NameExpr *string `json:"nameExpr,omitempty"` } // MapExpr is used for various fields to calculate map entries. Only one of // Key and KeyExpr may be specified; similarly only on of Value and ValueExpr // may be specified. type MapExpr struct { - Key *string `json:"key,omitempty"` - Value *string `json:"value,omitempty"` - KeyExpr *string `json:"keyExpr,omitempty"` - ValueExpr *string `json:"valueExpr,omitempty"` + Key *string `json:"key,omitempty"` + Value *string `json:"value,omitempty"` + KeyExpr *string `json:"keyExpr,omitempty"` + ValueExpr *string `json:"valueExpr,omitempty"` +} + +// PipelineTemplate is used to calculate the pipeline field of the resulting +// package variants. +type PipelineTemplate struct { + // Validators is used to caculate the pipeline.validators field of the + // resulting package variants. + // +optional + Validators []FunctionTemplate `json:"validators,omitempty"` + + // Mutators is used to caculate the pipeline.mutators field of the + // resulting package variants. + // +optional + Mutators []FunctionTemplate `json:"mutators,omitempty"` +} + +// FunctionTemplate is used in generating KRM function pipeline entries; that +// is, it is used to generate Kptfile Function objects. +type FunctionTemplate struct { + kptfilev1.Function `json:",inline"` + + // ConfigMapExprs allows use of CEL to dynamically create the keys and values in the + // function config ConfigMap. Entries in this field take precedent over those with + // the same keys that are present in ConfigMap. + // +optional + ConfigMapExprs []MapExpr `json:"configMapExprs,omitempty"` } ``` diff --git a/porch/controllers/config/crd/bases/config.porch.kpt.dev_packagevariants.yaml b/porch/controllers/config/crd/bases/config.porch.kpt.dev_packagevariants.yaml index ad102edc1..18f85d0b8 100644 --- a/porch/controllers/config/crd/bases/config.porch.kpt.dev_packagevariants.yaml +++ b/porch/controllers/config/crd/bases/config.porch.kpt.dev_packagevariants.yaml @@ -11,7 +11,6 @@ # 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. - --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -101,6 +100,223 @@ spec: type: string type: array type: object + pipeline: + description: Pipeline declares a pipeline of functions used to mutate + or validate resources. + properties: + mutators: + description: Mutators defines a list of of KRM functions that + mutate resources. + items: + description: Function specifies a KRM function. + properties: + configMap: + additionalProperties: + type: string + description: '`ConfigMap` is a convenient way to specify + a function config of kind ConfigMap.' + type: object + configPath: + description: '`ConfigPath` specifies a slash-delimited relative + path to a file in the current directory containing a KRM + resource used as the function config. This resource is + excluded when resolving ''sources'', and as a result cannot + be operated on by the pipeline.' + type: string + exclude: + description: '`Exclude` are used to specify resources on + which the function should NOT be executed. If not specified, + all resources selected by `Selectors` are selected.' + items: + description: Selector specifies the selection criteria + please update IsEmpty method if more properties are + added + properties: + annotations: + additionalProperties: + type: string + description: Annotations on the target resources + type: object + apiVersion: + description: APIVersion of the target resources + type: string + kind: + description: Kind of the target resources + type: string + labels: + additionalProperties: + type: string + description: Labels on the target resources + type: object + name: + description: Name of the target resources + type: string + namespace: + description: Namespace of the target resources + type: string + type: object + type: array + exec: + description: "Exec specifies the function binary executable. + The executable can be fully qualified or it must exists + in the $PATH e.g: \n exec: set-namespace exec: /usr/local/bin/my-custom-fn" + type: string + image: + description: "`Image` specifies the function container image. + It can either be fully qualified, e.g.: \n image: gcr.io/kpt-fn/set-labels + \n Optionally, kpt can be configured to use a image registry + host-path that will be used to resolve the image path + in case the image path is missing (Defaults to gcr.io/kpt-fn). + e.g. The following resolves to gcr.io/kpt-fn/set-labels: + \n image: set-labels" + type: string + name: + description: '`Name` is used to uniquely identify the function + declaration this is primarily used for merging function + declaration with upstream counterparts' + type: string + selectors: + description: '`Selectors` are used to specify resources + on which the function should be executed if not specified, + all resources are selected' + items: + description: Selector specifies the selection criteria + please update IsEmpty method if more properties are + added + properties: + annotations: + additionalProperties: + type: string + description: Annotations on the target resources + type: object + apiVersion: + description: APIVersion of the target resources + type: string + kind: + description: Kind of the target resources + type: string + labels: + additionalProperties: + type: string + description: Labels on the target resources + type: object + name: + description: Name of the target resources + type: string + namespace: + description: Namespace of the target resources + type: string + type: object + type: array + type: object + type: array + validators: + description: Validators defines a list of KRM functions that validate + resources. Validators are not permitted to mutate resources. + items: + description: Function specifies a KRM function. + properties: + configMap: + additionalProperties: + type: string + description: '`ConfigMap` is a convenient way to specify + a function config of kind ConfigMap.' + type: object + configPath: + description: '`ConfigPath` specifies a slash-delimited relative + path to a file in the current directory containing a KRM + resource used as the function config. This resource is + excluded when resolving ''sources'', and as a result cannot + be operated on by the pipeline.' + type: string + exclude: + description: '`Exclude` are used to specify resources on + which the function should NOT be executed. If not specified, + all resources selected by `Selectors` are selected.' + items: + description: Selector specifies the selection criteria + please update IsEmpty method if more properties are + added + properties: + annotations: + additionalProperties: + type: string + description: Annotations on the target resources + type: object + apiVersion: + description: APIVersion of the target resources + type: string + kind: + description: Kind of the target resources + type: string + labels: + additionalProperties: + type: string + description: Labels on the target resources + type: object + name: + description: Name of the target resources + type: string + namespace: + description: Namespace of the target resources + type: string + type: object + type: array + exec: + description: "Exec specifies the function binary executable. + The executable can be fully qualified or it must exists + in the $PATH e.g: \n exec: set-namespace exec: /usr/local/bin/my-custom-fn" + type: string + image: + description: "`Image` specifies the function container image. + It can either be fully qualified, e.g.: \n image: gcr.io/kpt-fn/set-labels + \n Optionally, kpt can be configured to use a image registry + host-path that will be used to resolve the image path + in case the image path is missing (Defaults to gcr.io/kpt-fn). + e.g. The following resolves to gcr.io/kpt-fn/set-labels: + \n image: set-labels" + type: string + name: + description: '`Name` is used to uniquely identify the function + declaration this is primarily used for merging function + declaration with upstream counterparts' + type: string + selectors: + description: '`Selectors` are used to specify resources + on which the function should be executed if not specified, + all resources are selected' + items: + description: Selector specifies the selection criteria + please update IsEmpty method if more properties are + added + properties: + annotations: + additionalProperties: + type: string + description: Annotations on the target resources + type: object + apiVersion: + description: APIVersion of the target resources + type: string + kind: + description: Kind of the target resources + type: string + labels: + additionalProperties: + type: string + description: Labels on the target resources + type: object + name: + description: Name of the target resources + type: string + namespace: + description: Namespace of the target resources + type: string + type: object + type: array + type: object + type: array + type: object upstream: properties: package: diff --git a/porch/controllers/config/crd/bases/config.porch.kpt.dev_packagevariantsets.yaml b/porch/controllers/config/crd/bases/config.porch.kpt.dev_packagevariantsets.yaml index db40b0d4c..bcdc81f34 100644 --- a/porch/controllers/config/crd/bases/config.porch.kpt.dev_packagevariantsets.yaml +++ b/porch/controllers/config/crd/bases/config.porch.kpt.dev_packagevariantsets.yaml @@ -316,7 +316,7 @@ spec: type: array type: object type: object - served: true + served: false storage: false subresources: status: {} @@ -599,6 +599,281 @@ spec: type: string type: array type: object + pipeline: + description: Pipeline allows specifying the spec.Pipeline + field of the generated PackageVariant + properties: + mutators: + description: Mutators is used to caculate the pipeline.mutators + field of the resulting package variants. + items: + description: FunctionTemplate is used in generating + KRM function pipeline entries; that is, it is used + to generate Kptfile Function objects. + properties: + configMap: + additionalProperties: + type: string + description: '`ConfigMap` is a convenient way + to specify a function config of kind ConfigMap.' + type: object + configMapExprs: + description: ConfigMapExprs allows use of CEL + to dynamically create the keys and values in + the function config ConfigMap. Entries in this + field take precedent over those with the same + keys that are present in ConfigMap. + items: + description: MapExpr is used for various fields + to calculate map entries. Only one of Key + and KeyExpr may be specified; similarly only + on of Value and ValueExpr may be specified. + properties: + key: + type: string + keyExpr: + type: string + value: + type: string + valueExpr: + type: string + type: object + type: array + configPath: + description: '`ConfigPath` specifies a slash-delimited + relative path to a file in the current directory + containing a KRM resource used as the function + config. This resource is excluded when resolving + ''sources'', and as a result cannot be operated + on by the pipeline.' + type: string + exclude: + description: '`Exclude` are used to specify resources + on which the function should NOT be executed. + If not specified, all resources selected by + `Selectors` are selected.' + items: + description: Selector specifies the selection + criteria please update IsEmpty method if more + properties are added + properties: + annotations: + additionalProperties: + type: string + description: Annotations on the target resources + type: object + apiVersion: + description: APIVersion of the target resources + type: string + kind: + description: Kind of the target resources + type: string + labels: + additionalProperties: + type: string + description: Labels on the target resources + type: object + name: + description: Name of the target resources + type: string + namespace: + description: Namespace of the target resources + type: string + type: object + type: array + exec: + description: "Exec specifies the function binary + executable. The executable can be fully qualified + or it must exists in the $PATH e.g: \n exec: + set-namespace exec: /usr/local/bin/my-custom-fn" + type: string + image: + description: "`Image` specifies the function container + image. It can either be fully qualified, e.g.: + \n image: gcr.io/kpt-fn/set-labels \n Optionally, + kpt can be configured to use a image registry + host-path that will be used to resolve the image + path in case the image path is missing (Defaults + to gcr.io/kpt-fn). e.g. The following resolves + to gcr.io/kpt-fn/set-labels: \n image: set-labels" + type: string + name: + description: '`Name` is used to uniquely identify + the function declaration this is primarily used + for merging function declaration with upstream + counterparts' + type: string + selectors: + description: '`Selectors` are used to specify + resources on which the function should be executed + if not specified, all resources are selected' + items: + description: Selector specifies the selection + criteria please update IsEmpty method if more + properties are added + properties: + annotations: + additionalProperties: + type: string + description: Annotations on the target resources + type: object + apiVersion: + description: APIVersion of the target resources + type: string + kind: + description: Kind of the target resources + type: string + labels: + additionalProperties: + type: string + description: Labels on the target resources + type: object + name: + description: Name of the target resources + type: string + namespace: + description: Namespace of the target resources + type: string + type: object + type: array + type: object + type: array + validators: + description: Validators is used to caculate the pipeline.validators + field of the resulting package variants. + items: + description: FunctionTemplate is used in generating + KRM function pipeline entries; that is, it is used + to generate Kptfile Function objects. + properties: + configMap: + additionalProperties: + type: string + description: '`ConfigMap` is a convenient way + to specify a function config of kind ConfigMap.' + type: object + configMapExprs: + description: ConfigMapExprs allows use of CEL + to dynamically create the keys and values in + the function config ConfigMap. Entries in this + field take precedent over those with the same + keys that are present in ConfigMap. + items: + description: MapExpr is used for various fields + to calculate map entries. Only one of Key + and KeyExpr may be specified; similarly only + on of Value and ValueExpr may be specified. + properties: + key: + type: string + keyExpr: + type: string + value: + type: string + valueExpr: + type: string + type: object + type: array + configPath: + description: '`ConfigPath` specifies a slash-delimited + relative path to a file in the current directory + containing a KRM resource used as the function + config. This resource is excluded when resolving + ''sources'', and as a result cannot be operated + on by the pipeline.' + type: string + exclude: + description: '`Exclude` are used to specify resources + on which the function should NOT be executed. + If not specified, all resources selected by + `Selectors` are selected.' + items: + description: Selector specifies the selection + criteria please update IsEmpty method if more + properties are added + properties: + annotations: + additionalProperties: + type: string + description: Annotations on the target resources + type: object + apiVersion: + description: APIVersion of the target resources + type: string + kind: + description: Kind of the target resources + type: string + labels: + additionalProperties: + type: string + description: Labels on the target resources + type: object + name: + description: Name of the target resources + type: string + namespace: + description: Namespace of the target resources + type: string + type: object + type: array + exec: + description: "Exec specifies the function binary + executable. The executable can be fully qualified + or it must exists in the $PATH e.g: \n exec: + set-namespace exec: /usr/local/bin/my-custom-fn" + type: string + image: + description: "`Image` specifies the function container + image. It can either be fully qualified, e.g.: + \n image: gcr.io/kpt-fn/set-labels \n Optionally, + kpt can be configured to use a image registry + host-path that will be used to resolve the image + path in case the image path is missing (Defaults + to gcr.io/kpt-fn). e.g. The following resolves + to gcr.io/kpt-fn/set-labels: \n image: set-labels" + type: string + name: + description: '`Name` is used to uniquely identify + the function declaration this is primarily used + for merging function declaration with upstream + counterparts' + type: string + selectors: + description: '`Selectors` are used to specify + resources on which the function should be executed + if not specified, all resources are selected' + items: + description: Selector specifies the selection + criteria please update IsEmpty method if more + properties are added + properties: + annotations: + additionalProperties: + type: string + description: Annotations on the target resources + type: object + apiVersion: + description: APIVersion of the target resources + type: string + kind: + description: Kind of the target resources + type: string + labels: + additionalProperties: + type: string + description: Labels on the target resources + type: object + name: + description: Name of the target resources + type: string + namespace: + description: Namespace of the target resources + type: string + type: object + type: array + type: object + type: array + type: object type: object type: object type: array diff --git a/porch/controllers/packagevariantsets/api/v1alpha1/packagevariantset_types.go b/porch/controllers/packagevariantsets/api/v1alpha1/packagevariantset_types.go index 4f8426656..953d3a097 100644 --- a/porch/controllers/packagevariantsets/api/v1alpha1/packagevariantset_types.go +++ b/porch/controllers/packagevariantsets/api/v1alpha1/packagevariantset_types.go @@ -20,9 +20,10 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status - +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:unservedversion +// // PackageVariantSet represents an upstream package revision and a way to // target specific downstream repositories where a variant of the upstream // package should be created. diff --git a/porch/controllers/packagevariantsets/api/v1alpha2/packagevariantset_types.go b/porch/controllers/packagevariantsets/api/v1alpha2/packagevariantset_types.go index 5925817dd..da8421d99 100644 --- a/porch/controllers/packagevariantsets/api/v1alpha2/packagevariantset_types.go +++ b/porch/controllers/packagevariantsets/api/v1alpha2/packagevariantset_types.go @@ -15,7 +15,7 @@ package v1alpha2 import ( - //kptfilev1 "github.com/GoogleContainerTools/kpt/pkg/api/kptfile/v1" + kptfilev1 "github.com/GoogleContainerTools/kpt/pkg/api/kptfile/v1" pkgvarapi "github.com/GoogleContainerTools/kpt/porch/controllers/packagevariants/api/v1alpha1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -114,7 +114,7 @@ type PackageVariantTemplate struct { // using CEL to dynamically create the keys and values. Entries in this field take precedent over // those with the same keys that are present in Labels. // +optional - LabelExprs []MapExpr `json:"labelExprs,omitemtpy"` + LabelExprs []MapExpr `json:"labelExprs,omitempty"` // Annotations allows specifying the spec.Annotations field of the generated PackageVariant // +optional @@ -132,7 +132,7 @@ type PackageVariantTemplate struct { // Pipeline allows specifying the spec.Pipeline field of the generated PackageVariant // +optional - //Pipeline *kptfilev1.Pipeline `json:"pipeline,omitempty"` + Pipeline *PipelineTemplate `json:"pipeline,omitempty"` // Injectors allows specifying the spec.Injectors field of the generated PackageVariant // +optional @@ -181,6 +181,32 @@ type MapExpr struct { ValueExpr *string `json:"valueExpr,omitempty"` } +// PipelineTemplate is used to calculate the pipeline field of the resulting +// package variants. +type PipelineTemplate struct { + // Validators is used to caculate the pipeline.validators field of the + // resulting package variants. + // +optional + Validators []FunctionTemplate `json:"validators,omitempty"` + + // Mutators is used to caculate the pipeline.mutators field of the + // resulting package variants. + // +optional + Mutators []FunctionTemplate `json:"mutators,omitempty"` +} + +// FunctionTemplate is used in generating KRM function pipeline entries; that +// is, it is used to generate Kptfile Function objects. +type FunctionTemplate struct { + kptfilev1.Function `json:",inline"` + + // ConfigMapExprs allows use of CEL to dynamically create the keys and values in the + // function config ConfigMap. Entries in this field take precedent over those with + // the same keys that are present in ConfigMap. + // +optional + ConfigMapExprs []MapExpr `json:"configMapExprs,omitempty"` +} + // PackageVariantSetStatus defines the observed state of PackageVariantSet type PackageVariantSetStatus struct { // Conditions describes the reconciliation state of the object. diff --git a/porch/controllers/packagevariantsets/pkg/controllers/packagevariantset/render.go b/porch/controllers/packagevariantsets/pkg/controllers/packagevariantset/render.go index 8483ab26e..97c3ec5c4 100644 --- a/porch/controllers/packagevariantsets/pkg/controllers/packagevariantset/render.go +++ b/porch/controllers/packagevariantsets/pkg/controllers/packagevariantset/render.go @@ -22,6 +22,7 @@ import ( "github.com/google/cel-go/cel" + kptfilev1 "github.com/GoogleContainerTools/kpt/pkg/api/kptfile/v1" porchapi "github.com/GoogleContainerTools/kpt/porch/api/porch/v1alpha1" configapi "github.com/GoogleContainerTools/kpt/porch/api/porchconfig/v1alpha1" pkgvarapi "github.com/GoogleContainerTools/kpt/porch/controllers/packagevariants/api/v1alpha1" @@ -163,9 +164,39 @@ func renderPackageVariantSpec(ctx context.Context, pvs *api.PackageVariantSet, r spec.Injectors = append(spec.Injectors, injector) } + if pvt.Pipeline != nil { + pipeline := kptfilev1.Pipeline{} + pipeline.Validators, err = renderFunctionTemplateList("template.pipeline.validators", pvt.Pipeline.Validators, inputs) + if err != nil { + return nil, err + } + pipeline.Mutators, err = renderFunctionTemplateList("template.pipeline.mutators", pvt.Pipeline.Mutators, inputs) + if err != nil { + return nil, err + } + if len(pipeline.Validators) > 0 || len(pipeline.Mutators) > 0 { + spec.Pipeline = &pipeline + } + } + return spec, nil } +func renderFunctionTemplateList(field string, templateList []api.FunctionTemplate, inputs map[string]interface{}) ([]kptfilev1.Function, error) { + var results []kptfilev1.Function + for i, ft := range templateList { + var err error + f := ft.Function + f.ConfigMap, err = copyAndOverlayMapExpr(fmt.Sprintf("%s[%d].configMapExprs", field, i), ft.ConfigMap, ft.ConfigMapExprs, inputs) + if err != nil { + return nil, err + } + + results = append(results, f) + } + return results, nil +} + func buildBaseInputs(upstreamPR *porchapi.PackageRevision, downstream pvContext) (map[string]interface{}, error) { inputs := make(map[string]interface{}, 5) inputs[RepoDefaultVarName] = downstream.repoDefault diff --git a/porch/controllers/packagevariantsets/pkg/controllers/packagevariantset/render_test.go b/porch/controllers/packagevariantsets/pkg/controllers/packagevariantset/render_test.go index 92442585c..187e001f2 100644 --- a/porch/controllers/packagevariantsets/pkg/controllers/packagevariantset/render_test.go +++ b/porch/controllers/packagevariantsets/pkg/controllers/packagevariantset/render_test.go @@ -18,6 +18,7 @@ import ( "context" "testing" + kptfilev1 "github.com/GoogleContainerTools/kpt/pkg/api/kptfile/v1" porchapi "github.com/GoogleContainerTools/kpt/porch/api/porch/v1alpha1" configapi "github.com/GoogleContainerTools/kpt/porch/api/porchconfig/v1alpha1" pkgvarapi "github.com/GoogleContainerTools/kpt/porch/controllers/packagevariants/api/v1alpha1" @@ -396,6 +397,88 @@ func TestRenderPackageVariantSpec(t *testing.T) { }, expectedErrs: nil, }, + "pipeline injectors": { + downstream: pvContext{ + repoDefault: "my-repo-1", + packageDefault: "p", + template: &api.PackageVariantTemplate{ + Pipeline: &api.PipelineTemplate{ + Validators: []api.FunctionTemplate{ + { + Function: kptfilev1.Function{ + Image: "foo:bar", + Name: "hey", + }, + }, + { + Function: kptfilev1.Function{ + Image: "foo:bar", + ConfigMap: map[string]string{ + "k1": "v1", + "k2": "v2", + }, + }, + ConfigMapExprs: []api.MapExpr{ + { + Key: pointer.String("k1"), + ValueExpr: pointer.String("repository.name"), + }, + { + KeyExpr: pointer.String("'k3'"), + Value: pointer.String("bar"), + }, + }, + }, + }, + Mutators: []api.FunctionTemplate{ + { + Function: kptfilev1.Function{ + Image: "mutates", + }, + ConfigMapExprs: []api.MapExpr{ + { + Key: pointer.String("k1"), + Value: pointer.String("yo"), + }, + }, + }, + }, + }, + }, + }, + expectedSpec: pkgvarapi.PackageVariantSpec{ + Upstream: pvs.Spec.Upstream, + Downstream: &pkgvarapi.Downstream{ + Repo: "my-repo-1", + Package: "p", + }, + Pipeline: &kptfilev1.Pipeline{ + Validators: []kptfilev1.Function{ + { + Image: "foo:bar", + Name: "hey", + }, + { + Image: "foo:bar", + ConfigMap: map[string]string{ + "k1": "my-repo-1", + "k2": "v2", + "k3": "bar", + }, + }, + }, + Mutators: []kptfilev1.Function{ + { + Image: "mutates", + ConfigMap: map[string]string{ + "k1": "yo", + }, + }, + }, + }, + }, + expectedErrs: nil, + }, } for tn, tc := range testCases { diff --git a/porch/controllers/packagevariantsets/pkg/controllers/packagevariantset/validate.go b/porch/controllers/packagevariantsets/pkg/controllers/packagevariantset/validate.go index e11f9fdd8..b1092bebb 100644 --- a/porch/controllers/packagevariantsets/pkg/controllers/packagevariantset/validate.go +++ b/porch/controllers/packagevariantsets/pkg/controllers/packagevariantset/validate.go @@ -140,6 +140,16 @@ func validateTemplate(template *api.PackageVariantTemplate, field string) []erro allErrs = append(allErrs, fmt.Errorf("%s.injectors[%d] must specify either `name` or `nameExpr`", field, i)) } } + + if template.Pipeline != nil { + for i, f := range template.Pipeline.Validators { + allErrs = append(allErrs, validateFunction(&f, fmt.Sprintf("%s.pipeline.validators[%d]", field, i))...) + } + for i, f := range template.Pipeline.Mutators { + allErrs = append(allErrs, validateFunction(&f, fmt.Sprintf("%s.pipeline.mutators[%d]", field, i))...) + } + } + return allErrs } @@ -157,6 +167,17 @@ func validateMapExpr(m []api.MapExpr, fieldName string) []error { return allErrs } +func validateFunction(f *api.FunctionTemplate, field string) []error { + var allErrs []error + if f.Image == "" { + allErrs = append(allErrs, fmt.Errorf("%s.image must not be empty", field)) + } + if strings.Contains(f.Name, ".") { + allErrs = append(allErrs, fmt.Errorf("%s.name must not contain '.'", field)) + } + return allErrs +} + func combineErrors(errs []error) string { var errMsgs []string for _, e := range errs { diff --git a/porch/controllers/packagevariantsets/pkg/controllers/packagevariantset/validate_test.go b/porch/controllers/packagevariantsets/pkg/controllers/packagevariantset/validate_test.go index 8a1740c0e..7a6b1abd4 100644 --- a/porch/controllers/packagevariantsets/pkg/controllers/packagevariantset/validate_test.go +++ b/porch/controllers/packagevariantsets/pkg/controllers/packagevariantset/validate_test.go @@ -186,6 +186,27 @@ spec: "spec.targets[0].template.injectors[1] must specify either `name` or `nameExpr`", }, }, + "pipeline function must be valid": { + packageVariant: packageVariantHeader + ` +spec: + targets: + - repositories: + - name: bar + template: + pipeline: + validators: + - name: foo + - image: foo + name: bar + mutators: + - name: foo.bar + image: bar +`, + expectedErrs: []string{"spec.upstream is a required field", + "spec.targets[0].template.pipeline.validators[0].image must not be empty", + "spec.targets[0].template.pipeline.mutators[0].name must not contain '.'", + }, + }, } for tn, tc := range testCases {