Repository: openshift/assisted-service (language: python)

Issue:
MGMT-17775: Ensure that patch manifest filenames are consistent and correct.
This PR addresses a few bugs in docs, regexes and code that cause inconsistency in the handling of patch manifests.

Patch manifests should end with the extension `.patch` or should end with a suffixed patch extension such as `.patch_extension`

## List all the issues related to this PR

- [ ] New Feature <!-- new functionality -->
- [ ] Enhancement <!-- refactor, code changes, improvement, that won't add new features -->
- [x] Bug fix
- [ ] Tests
- [x] Documentation
- [ ] CI/CD <!-- Notice that changes for Dockerfiles/Jenkinsfiles aren't tested in CI due to a known bug. -->

## What environments does this code impact?

- [x] Automation (CI, tools, etc)
- [x] Cloud
- [x] Operator Managed Deployments
- [x] None

## How was this code tested?

<!-- Please, select one or more if needed: -->

- [ ] assisted-test-infra environment
- [ ] dev-scripts environment
- [ ] Reviewer's test appreciated
- [x] Waiting for CI to do a full test run
- [ ] Manual (Elaborate on how it was tested)
- [x] No tests needed

## Checklist

- [x] Title and description added to both, commit and PR.
- [x] Relevant issues have been associated (see [CONTRIBUTING] guide)
- [x] This change does not require a documentation update (docstring, `docs`, README, etc)
- [x] Does this change include unit-tests (note that code changes require unit-tests)

## Reviewers Checklist

- Are the title and description (in both PR and commit) meaningful and clear?
- Is there a bug required (and linked) for this change?
- Should this PR be backported?

[Kubernetes community documentation]: https://github.com/kubernetes/community/blob/master/contributors/guide/pull-requests.md#commit-message-guidelines
[CONTRIBUTING]: https://github.com/openshift/assisted-service/blob/master/CONTRIBUTING.md



Discussion / hints:

Fix patch:
diff --git a/api/vendor/github.com/openshift/assisted-service/models/create_manifest_params.go b/api/vendor/github.com/openshift/assisted-service/models/create_manifest_params.go
index 6bd6bd2bdc9..edbaa3ab75c 100644
--- a/api/vendor/github.com/openshift/assisted-service/models/create_manifest_params.go
+++ b/api/vendor/github.com/openshift/assisted-service/models/create_manifest_params.go
@@ -26,7 +26,7 @@ type CreateManifestParams struct {
 
 	// The name of the manifest to customize the installed OCP cluster.
 	// Required: true
-	// Pattern: ^[^/]*\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$
+	// Pattern: ^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$
 	FileName *string `json:"file_name"`
 
 	// The folder that contains the files. Manifests can be placed in 'manifests' or 'openshift' directories.
@@ -71,7 +71,7 @@ func (m *CreateManifestParams) validateFileName(formats strfmt.Registry) error {
 		return err
 	}
 
-	if err := validate.Pattern("file_name", "body", *m.FileName, `^[^/]*\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$`); err != nil {
+	if err := validate.Pattern("file_name", "body", *m.FileName, `^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$`); err != nil {
 		return err
 	}
 
diff --git a/api/vendor/github.com/openshift/assisted-service/models/update_manifest_params.go b/api/vendor/github.com/openshift/assisted-service/models/update_manifest_params.go
index 662d9a5aede..9606db2aa74 100644
--- a/api/vendor/github.com/openshift/assisted-service/models/update_manifest_params.go
+++ b/api/vendor/github.com/openshift/assisted-service/models/update_manifest_params.go
@@ -22,7 +22,7 @@ type UpdateManifestParams struct {
 
 	// The file name for the manifest to modify.
 	// Required: true
-	// Pattern: ^[^/]*\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$
+	// Pattern: ^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$
 	FileName string `json:"file_name"`
 
 	// The folder for the manifest to modify.
@@ -34,7 +34,7 @@ type UpdateManifestParams struct {
 	UpdatedContent *string `json:"updated_content,omitempty"`
 
 	// The new file name for the manifest.
-	// Pattern: ^[^/]*\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$
+	// Pattern: ^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$
 	UpdatedFileName *string `json:"updated_file_name,omitempty"`
 
 	// The new folder for the manifest. Manifests can be placed in 'manifests' or 'openshift' directories.
@@ -74,7 +74,7 @@ func (m *UpdateManifestParams) validateFileName(formats strfmt.Registry) error {
 		return err
 	}
 
-	if err := validate.Pattern("file_name", "body", m.FileName, `^[^/]*\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$`); err != nil {
+	if err := validate.Pattern("file_name", "body", m.FileName, `^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$`); err != nil {
 		return err
 	}
 
@@ -129,7 +129,7 @@ func (m *UpdateManifestParams) validateUpdatedFileName(formats strfmt.Registry)
 		return nil
 	}
 
-	if err := validate.Pattern("updated_file_name", "body", *m.UpdatedFileName, `^[^/]*\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$`); err != nil {
+	if err := validate.Pattern("updated_file_name", "body", *m.UpdatedFileName, `^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$`); err != nil {
 		return err
 	}
 
diff --git a/client/vendor/github.com/openshift/assisted-service/models/create_manifest_params.go b/client/vendor/github.com/openshift/assisted-service/models/create_manifest_params.go
index 6bd6bd2bdc9..edbaa3ab75c 100644
--- a/client/vendor/github.com/openshift/assisted-service/models/create_manifest_params.go
+++ b/client/vendor/github.com/openshift/assisted-service/models/create_manifest_params.go
@@ -26,7 +26,7 @@ type CreateManifestParams struct {
 
 	// The name of the manifest to customize the installed OCP cluster.
 	// Required: true
-	// Pattern: ^[^/]*\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$
+	// Pattern: ^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$
 	FileName *string `json:"file_name"`
 
 	// The folder that contains the files. Manifests can be placed in 'manifests' or 'openshift' directories.
@@ -71,7 +71,7 @@ func (m *CreateManifestParams) validateFileName(formats strfmt.Registry) error {
 		return err
 	}
 
-	if err := validate.Pattern("file_name", "body", *m.FileName, `^[^/]*\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$`); err != nil {
+	if err := validate.Pattern("file_name", "body", *m.FileName, `^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$`); err != nil {
 		return err
 	}
 
diff --git a/client/vendor/github.com/openshift/assisted-service/models/update_manifest_params.go b/client/vendor/github.com/openshift/assisted-service/models/update_manifest_params.go
index 662d9a5aede..9606db2aa74 100644
--- a/client/vendor/github.com/openshift/assisted-service/models/update_manifest_params.go
+++ b/client/vendor/github.com/openshift/assisted-service/models/update_manifest_params.go
@@ -22,7 +22,7 @@ type UpdateManifestParams struct {
 
 	// The file name for the manifest to modify.
 	// Required: true
-	// Pattern: ^[^/]*\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$
+	// Pattern: ^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$
 	FileName string `json:"file_name"`
 
 	// The folder for the manifest to modify.
@@ -34,7 +34,7 @@ type UpdateManifestParams struct {
 	UpdatedContent *string `json:"updated_content,omitempty"`
 
 	// The new file name for the manifest.
-	// Pattern: ^[^/]*\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$
+	// Pattern: ^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$
 	UpdatedFileName *string `json:"updated_file_name,omitempty"`
 
 	// The new folder for the manifest. Manifests can be placed in 'manifests' or 'openshift' directories.
@@ -74,7 +74,7 @@ func (m *UpdateManifestParams) validateFileName(formats strfmt.Registry) error {
 		return err
 	}
 
-	if err := validate.Pattern("file_name", "body", m.FileName, `^[^/]*\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$`); err != nil {
+	if err := validate.Pattern("file_name", "body", m.FileName, `^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$`); err != nil {
 		return err
 	}
 
@@ -129,7 +129,7 @@ func (m *UpdateManifestParams) validateUpdatedFileName(formats strfmt.Registry)
 		return nil
 	}
 
-	if err := validate.Pattern("updated_file_name", "body", *m.UpdatedFileName, `^[^/]*\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$`); err != nil {
+	if err := validate.Pattern("updated_file_name", "body", *m.UpdatedFileName, `^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$`); err != nil {
 		return err
 	}
 
diff --git a/docs/user-guide/install-customization.md b/docs/user-guide/install-customization.md
index 53d873fff27..15ec63fa17a 100644
--- a/docs/user-guide/install-customization.md
+++ b/docs/user-guide/install-customization.md
@@ -9,7 +9,8 @@ These will only take effect after the machine config operator is up and running
 Formats json, yaml, and multi-document yaml are accepted as manifests.
 Multi-document yaml manifests are split with unique names to avoid any collisions with existing files when they are added to their respective folders (either "manifests" or "openshift").
 
-Yaml patches are also accepted and should be in the yaml-patch format, patches should contain `.yaml.patch.` or `.yml.patch.` as a part of their filename.
+Yaml patches are also accepted and should be in the yaml-patch format, patches should end with `.yml.patch`, `yaml.patch`.
+Extensions may have a suffix of an underscore followed by some alphanumeric characters, for example `.yml.patch_ocp_manifests` or `.yaml.patch_ocp_manifests`
 Here is an example of the format of a Yaml patch file, for more information on the format, please see https://github.com/krishicks/yaml-patch
 
 ```
diff --git a/internal/manifests/manifests.go b/internal/manifests/manifests.go
index 9b99a1edc80..671fb01db70 100644
--- a/internal/manifests/manifests.go
+++ b/internal/manifests/manifests.go
@@ -541,7 +541,7 @@ func (m *Manifests) validateUserSuppliedManifest(ctx context.Context, clusterID
 		if !json.Valid(manifestContent) {
 			return m.prepareAndLogError(ctx, http.StatusBadRequest, errors.Errorf("Manifest content of file %s for cluster ID %s has an illegal JSON format", fileName, string(clusterID)))
 		}
-	} else if strings.Contains(fileName, ".yaml.patch") || strings.Contains(fileName, ".yml.patch") {
+	} else if strings.HasPrefix(extension, ".patch") {
 		if _, err := yamlpatch.DecodePatch(manifestContent); err != nil {
 			return m.prepareAndLogError(ctx, http.StatusBadRequest, errors.Errorf("Patch content of file %s for cluster ID %s is invalid: %s", fileName, string(clusterID), err))
 		}
diff --git a/models/create_manifest_params.go b/models/create_manifest_params.go
index 6bd6bd2bdc9..edbaa3ab75c 100644
--- a/models/create_manifest_params.go
+++ b/models/create_manifest_params.go
@@ -26,7 +26,7 @@ type CreateManifestParams struct {
 
 	// The name of the manifest to customize the installed OCP cluster.
 	// Required: true
-	// Pattern: ^[^/]*\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$
+	// Pattern: ^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$
 	FileName *string `json:"file_name"`
 
 	// The folder that contains the files. Manifests can be placed in 'manifests' or 'openshift' directories.
@@ -71,7 +71,7 @@ func (m *CreateManifestParams) validateFileName(formats strfmt.Registry) error {
 		return err
 	}
 
-	if err := validate.Pattern("file_name", "body", *m.FileName, `^[^/]*\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$`); err != nil {
+	if err := validate.Pattern("file_name", "body", *m.FileName, `^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$`); err != nil {
 		return err
 	}
 
diff --git a/models/update_manifest_params.go b/models/update_manifest_params.go
index 662d9a5aede..9606db2aa74 100644
--- a/models/update_manifest_params.go
+++ b/models/update_manifest_params.go
@@ -22,7 +22,7 @@ type UpdateManifestParams struct {
 
 	// The file name for the manifest to modify.
 	// Required: true
-	// Pattern: ^[^/]*\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$
+	// Pattern: ^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$
 	FileName string `json:"file_name"`
 
 	// The folder for the manifest to modify.
@@ -34,7 +34,7 @@ type UpdateManifestParams struct {
 	UpdatedContent *string `json:"updated_content,omitempty"`
 
 	// The new file name for the manifest.
-	// Pattern: ^[^/]*\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$
+	// Pattern: ^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$
 	UpdatedFileName *string `json:"updated_file_name,omitempty"`
 
 	// The new folder for the manifest. Manifests can be placed in 'manifests' or 'openshift' directories.
@@ -74,7 +74,7 @@ func (m *UpdateManifestParams) validateFileName(formats strfmt.Registry) error {
 		return err
 	}
 
-	if err := validate.Pattern("file_name", "body", m.FileName, `^[^/]*\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$`); err != nil {
+	if err := validate.Pattern("file_name", "body", m.FileName, `^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$`); err != nil {
 		return err
 	}
 
@@ -129,7 +129,7 @@ func (m *UpdateManifestParams) validateUpdatedFileName(formats strfmt.Registry)
 		return nil
 	}
 
-	if err := validate.Pattern("updated_file_name", "body", *m.UpdatedFileName, `^[^/]*\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$`); err != nil {
+	if err := validate.Pattern("updated_file_name", "body", *m.UpdatedFileName, `^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$`); err != nil {
 		return err
 	}
 
diff --git a/restapi/embedded_spec.go b/restapi/embedded_spec.go
index 1959f869957..437ed494ef6 100644
--- a/restapi/embedded_spec.go
+++ b/restapi/embedded_spec.go
@@ -7179,7 +7179,7 @@ func init() {
         "file_name": {
           "description": "The name of the manifest to customize the installed OCP cluster.",
           "type": "string",
-          "pattern": "^[^/]*\\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$"
+          "pattern": "^[^\\/]*\\.(json|ya?ml(\\.patch_?[a-zA-Z0-9_]*)?)$"
         },
         "folder": {
           "description": "The folder that contains the files. Manifests can be placed in 'manifests' or 'openshift' directories.",
@@ -10231,7 +10231,7 @@ func init() {
         "file_name": {
           "description": "The file name for the manifest to modify.",
           "type": "string",
-          "pattern": "^[^/]*\\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$",
+          "pattern": "^[^\\/]*\\.(json|ya?ml(\\.patch_?[a-zA-Z0-9_]*)?)$",
           "x-nullable": false
         },
         "folder": {
@@ -10252,7 +10252,7 @@ func init() {
         "updated_file_name": {
           "description": "The new file name for the manifest.",
           "type": "string",
-          "pattern": "^[^/]*\\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$",
+          "pattern": "^[^\\/]*\\.(json|ya?ml(\\.patch_?[a-zA-Z0-9_]*)?)$",
           "x-nullable": true
         },
         "updated_folder": {
@@ -17957,7 +17957,7 @@ func init() {
         "file_name": {
           "description": "The name of the manifest to customize the installed OCP cluster.",
           "type": "string",
-          "pattern": "^[^/]*\\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$"
+          "pattern": "^[^\\/]*\\.(json|ya?ml(\\.patch_?[a-zA-Z0-9_]*)?)$"
         },
         "folder": {
           "description": "The folder that contains the files. Manifests can be placed in 'manifests' or 'openshift' directories.",
@@ -20948,7 +20948,7 @@ func init() {
         "file_name": {
           "description": "The file name for the manifest to modify.",
           "type": "string",
-          "pattern": "^[^/]*\\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$",
+          "pattern": "^[^\\/]*\\.(json|ya?ml(\\.patch_?[a-zA-Z0-9_]*)?)$",
           "x-nullable": false
         },
         "folder": {
@@ -20969,7 +20969,7 @@ func init() {
         "updated_file_name": {
           "description": "The new file name for the manifest.",
           "type": "string",
-          "pattern": "^[^/]*\\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$",
+          "pattern": "^[^\\/]*\\.(json|ya?ml(\\.patch_?[a-zA-Z0-9_]*)?)$",
           "x-nullable": true
         },
         "updated_folder": {
diff --git a/swagger.yaml b/swagger.yaml
index 4195fbc2a53..ce7747c4920 100644
--- a/swagger.yaml
+++ b/swagger.yaml
@@ -6901,7 +6901,7 @@ definitions:
       file_name:
         description: The name of the manifest to customize the installed OCP cluster.
         type: string
-        pattern: '^[^/]*\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$'
+        pattern: '^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$'
       content:
         description: base64 encoded manifest content.
         type: string
@@ -6921,7 +6921,7 @@ definitions:
       file_name:
         description: The file name for the manifest to modify.
         type: string
-        pattern: '^[^/]*\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$'
+        pattern: '^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$'
         x-nullable: false
       updated_folder:
         description: The new folder for the manifest. Manifests can be placed in 'manifests' or 'openshift' directories.
@@ -6932,7 +6932,7 @@ definitions:
       updated_file_name:
         description: The new file name for the manifest.
         type: string
-        pattern: '^[^/]*\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$'
+        pattern: '^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$'
         x-nullable: true
       updated_content:
         description: The new base64 encoded manifest content.
diff --git a/vendor/github.com/openshift/assisted-service/models/create_manifest_params.go b/vendor/github.com/openshift/assisted-service/models/create_manifest_params.go
index 6bd6bd2bdc9..edbaa3ab75c 100644
--- a/vendor/github.com/openshift/assisted-service/models/create_manifest_params.go
+++ b/vendor/github.com/openshift/assisted-service/models/create_manifest_params.go
@@ -26,7 +26,7 @@ type CreateManifestParams struct {
 
 	// The name of the manifest to customize the installed OCP cluster.
 	// Required: true
-	// Pattern: ^[^/]*\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$
+	// Pattern: ^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$
 	FileName *string `json:"file_name"`
 
 	// The folder that contains the files. Manifests can be placed in 'manifests' or 'openshift' directories.
@@ -71,7 +71,7 @@ func (m *CreateManifestParams) validateFileName(formats strfmt.Registry) error {
 		return err
 	}
 
-	if err := validate.Pattern("file_name", "body", *m.FileName, `^[^/]*\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$`); err != nil {
+	if err := validate.Pattern("file_name", "body", *m.FileName, `^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$`); err != nil {
 		return err
 	}
 
diff --git a/vendor/github.com/openshift/assisted-service/models/update_manifest_params.go b/vendor/github.com/openshift/assisted-service/models/update_manifest_params.go
index 662d9a5aede..9606db2aa74 100644
--- a/vendor/github.com/openshift/assisted-service/models/update_manifest_params.go
+++ b/vendor/github.com/openshift/assisted-service/models/update_manifest_params.go
@@ -22,7 +22,7 @@ type UpdateManifestParams struct {
 
 	// The file name for the manifest to modify.
 	// Required: true
-	// Pattern: ^[^/]*\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$
+	// Pattern: ^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$
 	FileName string `json:"file_name"`
 
 	// The folder for the manifest to modify.
@@ -34,7 +34,7 @@ type UpdateManifestParams struct {
 	UpdatedContent *string `json:"updated_content,omitempty"`
 
 	// The new file name for the manifest.
-	// Pattern: ^[^/]*\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$
+	// Pattern: ^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$
 	UpdatedFileName *string `json:"updated_file_name,omitempty"`
 
 	// The new folder for the manifest. Manifests can be placed in 'manifests' or 'openshift' directories.
@@ -74,7 +74,7 @@ func (m *UpdateManifestParams) validateFileName(formats strfmt.Registry) error {
 		return err
 	}
 
-	if err := validate.Pattern("file_name", "body", m.FileName, `^[^/]*\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$`); err != nil {
+	if err := validate.Pattern("file_name", "body", m.FileName, `^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$`); err != nil {
 		return err
 	}
 
@@ -129,7 +129,7 @@ func (m *UpdateManifestParams) validateUpdatedFileName(formats strfmt.Registry)
 		return nil
 	}
 
-	if err := validate.Pattern("updated_file_name", "body", *m.UpdatedFileName, `^[^/]*\.(yaml|yml|json|yaml.patch.*|yml.patch.*)$`); err != nil {
+	if err := validate.Pattern("updated_file_name", "body", *m.UpdatedFileName, `^[^\/]*\.(json|ya?ml(\.patch_?[a-zA-Z0-9_]*)?)$`); err != nil {
 		return err
 	}
 


Test patch:
diff --git a/internal/manifests/manifests_test.go b/internal/manifests/manifests_test.go
index b5059e38902..7d1c3c3478e 100644
--- a/internal/manifests/manifests_test.go
+++ b/internal/manifests/manifests_test.go
@@ -73,8 +73,8 @@ var _ = Describe("ClusterManifestTests", func() {
 		mockS3Client      *s3wrapper.MockAPI
 		dbName            string
 		fileNameYaml      = "99-openshift-machineconfig-master-kargs.yaml"
-		fileNameYamlPatch = "99-openshift-machineconfig-master-kargs.yaml.patch.foobar"
-		fileNameYmlPatch  = "99-openshift-machineconfig-master-kargs.yml.patch.foobar"
+		fileNameYamlPatch = "99-openshift-machineconfig-master-kargs.yaml.patch_foobar"
+		fileNameYmlPatch  = "99-openshift-machineconfig-master-kargs.yml.patch_foobar"
 		fileNameJson      = "99-openshift-machineconfig-master-kargs.json"
 		validFolder       = "openshift"
 		defaultFolder     = "manifests"
@@ -506,6 +506,49 @@ spec:
 				Expect(response).Should(BeAssignableToTypeOf(operations.NewV2CreateClusterManifestCreated()))
 			})
 
+			It("manifest creation will succeed for patch files if filename is valid", func() {
+				clusterID := registerCluster().ID
+				validFileNames := []string{
+					"something.patch",
+					"something.patch_something_else",
+					"something.patch.patch_something",
+				}
+				for i, fileName := range validFileNames {
+					mockUpload(1)
+					expectUsageCalls()
+					mockS3Client.EXPECT().DoesObjectExist(ctx, filepath.Join(clusterID.String(), constants.ManifestFolder, "openshift", fileName)).Return(false, nil).AnyTimes()
+					response := manifestsAPI.V2CreateClusterManifest(ctx, operations.V2CreateClusterManifestParams{
+						ClusterID: *clusterID,
+						CreateManifestParams: &models.CreateManifestParams{
+							Content:  &contentYamlPatch,
+							FileName: &validFileNames[i],
+						},
+					})
+					Expect(response).Should(BeAssignableToTypeOf(operations.NewV2CreateClusterManifestCreated()))
+				}
+			})
+
+			It("manifest creation will fail for patch files if filename is invalid", func() {
+				clusterID := registerCluster().ID
+				invalidFileNames := []string{
+					"something.patch.something",
+					"something.patch.something.else",
+				}
+				for i, fileName := range invalidFileNames {
+					response := manifestsAPI.V2CreateClusterManifest(ctx, operations.V2CreateClusterManifestParams{
+						ClusterID: *clusterID,
+						CreateManifestParams: &models.CreateManifestParams{
+							Content:  &contentYaml,
+							FileName: &invalidFileNames[i],
+						},
+					})
+					Expect(response).Should(BeAssignableToTypeOf(common.NewApiError(http.StatusBadRequest, errors.New(""))))
+					err := response.(*common.ApiErrorResponse)
+					Expect(err.StatusCode()).To(Equal(int32(http.StatusBadRequest)))
+					Expect(err.Error()).To(ContainSubstring("Manifest filename of file manifests/%s for cluster ID %s is invalid. Only json, yaml and yml or patch extensions are supported", fileName, clusterID.String()))
+				}
+			})
+
 			It("fails for filename that contains folder in the name", func() {
 				clusterID := registerCluster().ID
 				fileNameWithFolder := "openshift/99-test.yaml"