Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Allow JSON merge patches for provider manifests #280

Merged
merged 5 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 93 additions & 8 deletions api/v1alpha1/provider_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

apimachineryconversion "k8s.io/apimachinery/pkg/conversion"
"k8s.io/utils/pointer"
utilconversion "sigs.k8s.io/cluster-api/util/conversion"
ctrlconfigv1 "sigs.k8s.io/controller-runtime/pkg/config/v1alpha1"
"sigs.k8s.io/controller-runtime/pkg/conversion"

Expand All @@ -34,7 +35,19 @@ func (src *BootstrapProvider) ConvertTo(dstRaw conversion.Hub) error {
panic("expected to get an of object of type v1alpha2.BootstrapProvider")
}

return Convert_v1alpha1_BootstrapProvider_To_v1alpha2_BootstrapProvider(src, dst, nil)
if err := Convert_v1alpha1_BootstrapProvider_To_v1alpha2_BootstrapProvider(src, dst, nil); err != nil {
return err
}

// Manually restore data.
restored := &operatorv1.BootstrapProvider{}
if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok {
return err
}

dst.Spec.ManifestPatches = restored.Spec.ManifestPatches

return nil
}

// ConvertFrom converts from the BootstrapProvider version (v1alpha2) to this version.
Expand All @@ -44,7 +57,16 @@ func (dst *BootstrapProvider) ConvertFrom(srcRaw conversion.Hub) error {
panic("expected to get an of object of type v1alpha2.BootstrapProvider")
}

return Convert_v1alpha2_BootstrapProvider_To_v1alpha1_BootstrapProvider(src, dst, nil)
if err := Convert_v1alpha2_BootstrapProvider_To_v1alpha1_BootstrapProvider(src, dst, nil); err != nil {
return err
}

// Preserve Hub data on down-conversion.
if err := utilconversion.MarshalData(src, dst); err != nil {
return err
}

return nil
}

// ConvertTo converts this BootstrapProviderList to the Hub version (v1alpha2).
Expand Down Expand Up @@ -74,7 +96,19 @@ func (src *ControlPlaneProvider) ConvertTo(dstRaw conversion.Hub) error {
panic("expected to get an of object of type v1alpha2.ControlPlaneProvider")
}

return Convert_v1alpha1_ControlPlaneProvider_To_v1alpha2_ControlPlaneProvider(src, dst, nil)
if err := Convert_v1alpha1_ControlPlaneProvider_To_v1alpha2_ControlPlaneProvider(src, dst, nil); err != nil {
return err
}

// Manually restore data.
restored := &operatorv1.ControlPlaneProvider{}
if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok {
return err
}

dst.Spec.ManifestPatches = restored.Spec.ManifestPatches

return nil
}

// ConvertFrom converts from the ControlPlaneProvider version (v1alpha2) to this version.
Expand All @@ -84,7 +118,16 @@ func (dst *ControlPlaneProvider) ConvertFrom(srcRaw conversion.Hub) error {
panic("expected to get an of object of type v1alpha2.ControlPlaneProvider")
}

return Convert_v1alpha2_ControlPlaneProvider_To_v1alpha1_ControlPlaneProvider(src, dst, nil)
if err := Convert_v1alpha2_ControlPlaneProvider_To_v1alpha1_ControlPlaneProvider(src, dst, nil); err != nil {
return err
}

// Preserve Hub data on down-conversion.
if err := utilconversion.MarshalData(src, dst); err != nil {
return err
}

return nil
}

// ConvertTo converts this ControlPlaneProviderList to the Hub version (v1alpha2).
Expand Down Expand Up @@ -114,7 +157,19 @@ func (src *CoreProvider) ConvertTo(dstRaw conversion.Hub) error {
panic("expected to get an of object of type v1alpha2.CoreProvider")
}

return Convert_v1alpha1_CoreProvider_To_v1alpha2_CoreProvider(src, dst, nil)
if err := Convert_v1alpha1_CoreProvider_To_v1alpha2_CoreProvider(src, dst, nil); err != nil {
return err
}

// Manually restore data.
restored := &operatorv1.CoreProvider{}
if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok {
return err
}

dst.Spec.ManifestPatches = restored.Spec.ManifestPatches

return nil
}

// ConvertFrom converts from the CoreProvider version (v1alpha2) to this version.
Expand All @@ -124,7 +179,16 @@ func (dst *CoreProvider) ConvertFrom(srcRaw conversion.Hub) error {
panic("expected to get an of object of type v1alpha2.CoreProvider")
}

return Convert_v1alpha2_CoreProvider_To_v1alpha1_CoreProvider(src, dst, nil)
if err := Convert_v1alpha2_CoreProvider_To_v1alpha1_CoreProvider(src, dst, nil); err != nil {
return err
}

// Preserve Hub data on down-conversion.
if err := utilconversion.MarshalData(src, dst); err != nil {
return err
}

return nil
}

// ConvertTo converts this CoreProviderList to the Hub version (v1alpha2).
Expand Down Expand Up @@ -154,7 +218,19 @@ func (src *InfrastructureProvider) ConvertTo(dstRaw conversion.Hub) error {
panic("expected to get an of object of type v1alpha2.InfrastructureProvider")
}

return Convert_v1alpha1_InfrastructureProvider_To_v1alpha2_InfrastructureProvider(src, dst, nil)
if err := Convert_v1alpha1_InfrastructureProvider_To_v1alpha2_InfrastructureProvider(src, dst, nil); err != nil {
return err
}

// Manually restore data.
restored := &operatorv1.InfrastructureProvider{}
if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok {
return err
}

dst.Spec.ManifestPatches = restored.Spec.ManifestPatches

return nil
}

// ConvertFrom converts from the InfrastructureProvider version (v1alpha2) to this version.
Expand All @@ -164,7 +240,16 @@ func (dst *InfrastructureProvider) ConvertFrom(srcRaw conversion.Hub) error {
panic("expected to get an of object of type v1alpha2.InfrastructureProvider")
}

return Convert_v1alpha2_InfrastructureProvider_To_v1alpha1_InfrastructureProvider(src, dst, nil)
if err := Convert_v1alpha2_InfrastructureProvider_To_v1alpha1_InfrastructureProvider(src, dst, nil); err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like we do not have a conversion for addon provider? Is it because that was introduced in v1alpha2?

Copy link
Contributor Author

@alexander-demicev alexander-demicev Oct 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, we don't have addons in v1alpha1

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah that is what I actually meant as well

return err
}

// Preserve Hub data on down-conversion.
if err := utilconversion.MarshalData(src, dst); err != nil {
return err
}

return nil
}

// ConvertTo converts this InfrastructureProviderList to the Hub version (v1alpha2).
Expand Down
1 change: 1 addition & 0 deletions api/v1alpha1/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions api/v1alpha2/provider_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ type ProviderSpec struct {
// namespace of the provider will be used. There is no validation of the yaml content inside the configmap.
// +optional
AdditionalManifestsRef *ConfigmapReference `json:"additionalManifests,omitempty"`

// ManifestPatches are applied to rendered provider manifests to customize the
// provider manifests. Patches are applied in the order they are specified.
// The `kind` field must match the target object, and
// if `apiVersion` is specified it will only be applied to matching objects.
// This should be an inline yaml blob-string https://datatracker.ietf.org/doc/html/rfc7396
// +optional
ManifestPatches []string `json:"manifestPatches,omitempty"`
}

// ConfigmapReference contains enough information to locate the configmap.
Expand Down
5 changes: 5 additions & 0 deletions api/v1alpha2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -1466,6 +1466,15 @@ spec:
type: integer
type: object
type: object
manifestPatches:
description: ManifestPatches are applied to rendered provider manifests
to customize the provider manifests. Patches are applied in the
order they are specified. The `kind` field must match the target
object, and if `apiVersion` is specified it will only be applied
to matching objects. This should be an inline yaml blob-string https://datatracker.ietf.org/doc/html/rfc7396
items:
type: string
type: array
version:
description: Version indicates the provider version.
type: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2995,6 +2995,15 @@ spec:
type: integer
type: object
type: object
manifestPatches:
description: ManifestPatches are applied to rendered provider manifests
to customize the provider manifests. Patches are applied in the
order they are specified. The `kind` field must match the target
object, and if `apiVersion` is specified it will only be applied
to matching objects. This should be an inline yaml blob-string https://datatracker.ietf.org/doc/html/rfc7396
items:
type: string
type: array
version:
description: Version indicates the provider version.
type: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2998,6 +2998,15 @@ spec:
type: integer
type: object
type: object
manifestPatches:
description: ManifestPatches are applied to rendered provider manifests
to customize the provider manifests. Patches are applied in the
order they are specified. The `kind` field must match the target
object, and if `apiVersion` is specified it will only be applied
to matching objects. This should be an inline yaml blob-string https://datatracker.ietf.org/doc/html/rfc7396
items:
type: string
type: array
version:
description: Version indicates the provider version.
type: string
Expand Down
9 changes: 9 additions & 0 deletions config/crd/bases/operator.cluster.x-k8s.io_coreproviders.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2995,6 +2995,15 @@ spec:
type: integer
type: object
type: object
manifestPatches:
description: ManifestPatches are applied to rendered provider manifests
to customize the provider manifests. Patches are applied in the
order they are specified. The `kind` field must match the target
object, and if `apiVersion` is specified it will only be applied
to matching objects. This should be an inline yaml blob-string https://datatracker.ietf.org/doc/html/rfc7396
items:
type: string
type: array
version:
description: Version indicates the provider version.
type: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2998,6 +2998,15 @@ spec:
type: integer
type: object
type: object
manifestPatches:
description: ManifestPatches are applied to rendered provider manifests
to customize the provider manifests. Patches are applied in the
order they are specified. The `kind` field must match the target
object, and if `apiVersion` is specified it will only be applied
to matching objects. This should be an inline yaml blob-string https://datatracker.ietf.org/doc/html/rfc7396
items:
type: string
type: array
version:
description: Version indicates the provider version.
type: string
Expand Down
32 changes: 32 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -754,3 +754,35 @@ spec:
additionalManifests:
name: additional-manifests
```

## Patching provider manifests
alexander-demicev marked this conversation as resolved.
Show resolved Hide resolved

Provider manifests can be patched using JSON merge patches. This can be useful when you need to modify the provider manifests that are fetched from the repository. In order to provider
manifests `spec.ResourcePatches` has to be used where an array of patches can be specified:

```yaml
---
apiVersion: operator.cluster.x-k8s.io/v1alpha2
kind: CoreProvider
metadata:
name: cluster-api
namespace: capi-system
spec:
resourcePatches:
- |
apiVersion: v1
kind: Service
metadata:
labels:
test-label: test-value
```

More information about JSON merge patches can be found here https://datatracker.ietf.org/doc/html/rfc7396

There are couple of rules for the patch to match a manifest:

- The `kind` field must match the target object.
- If `apiVersion` is specified it will only be applied to matching objects.
- If `metadata.name` and `metadata.namespace` not specified, the patch will be applied to all objects of the specified kind.
- If `metadata.name` is specified, the patch will be applied to the object with the specified name. This is for cluster scoped objects.
- If both `metadata.name` and `metadata.namespace` are specified, the patch will be applied to the object with the specified name and namespace.
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ replace sigs.k8s.io/cluster-api => sigs.k8s.io/cluster-api v1.5.1

require (
github.com/MakeNowJust/heredoc v1.0.0
github.com/evanphx/json-patch/v5 v5.6.0
github.com/google/go-cmp v0.6.0
github.com/google/go-github/v52 v52.0.0
github.com/google/gofuzz v1.2.0
Expand All @@ -23,6 +24,7 @@ require (
k8s.io/utils v0.0.0-20230209194617-a36077c30491
sigs.k8s.io/cluster-api v1.5.1
sigs.k8s.io/controller-runtime v0.15.2
sigs.k8s.io/yaml v1.3.0
)

require (
Expand All @@ -45,7 +47,6 @@ require (
github.com/drone/envsubst/v2 v2.0.0-20210730161058-179042472c46 // indirect
github.com/emicklei/go-restful/v3 v3.10.2 // indirect
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
Expand Down Expand Up @@ -109,5 +110,4 @@ require (
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
41 changes: 41 additions & 0 deletions internal/controller/component_patches.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
Copyright 2023 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package controller

import (
"context"

"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"sigs.k8s.io/cluster-api-operator/internal/controller/genericprovider"
"sigs.k8s.io/cluster-api-operator/internal/patch"
ctrl "sigs.k8s.io/controller-runtime"
)

func applyPatches(ctx context.Context, provider genericprovider.GenericProvider) func(objs []unstructured.Unstructured) ([]unstructured.Unstructured, error) {
log := ctrl.LoggerFrom(ctx)

return func(objs []unstructured.Unstructured) ([]unstructured.Unstructured, error) {
if len(provider.GetSpec().ManifestPatches) == 0 {
log.V(5).Info("No resource patches to apply")
return objs, nil
}

log.V(5).Info("Applying resource patches")

return patch.ApplyPatches(objs, provider.GetSpec().ManifestPatches)
}
}
Loading
Loading