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

Add and Enhance support for file sanitization and identification #659

Merged
merged 33 commits into from
Feb 3, 2025
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
e47062d
add file sanitization helpers
aabidsofi19 Jan 30, 2025
6668acc
add sanitization tests
aabidsofi19 Jan 30, 2025
2e29345
add test samples
aabidsofi19 Jan 30, 2025
27d8c67
add support for extracting nested tar
aabidsofi19 Jan 30, 2025
3832ef2
return raw data from after sanitization
aabidsofi19 Jan 30, 2025
347136d
add support for identifying designs
aabidsofi19 Jan 30, 2025
d8c2833
update tests
aabidsofi19 Jan 30, 2025
802bedb
update samples
aabidsofi19 Jan 30, 2025
ce20983
add support for identifying kubernetes manifests
aabidsofi19 Jan 30, 2025
c9e2475
add tests
aabidsofi19 Jan 30, 2025
1cf94bd
fix extraction
aabidsofi19 Jan 30, 2025
4824d90
add helm identification
aabidsofi19 Jan 30, 2025
16a5fe7
update samples
aabidsofi19 Jan 30, 2025
b7c6d44
add support for identifying kustomization
aabidsofi19 Jan 31, 2025
533b3c2
add tests
aabidsofi19 Jan 31, 2025
16b8c41
add support for zip
aabidsofi19 Jan 31, 2025
1b35853
fix temp dirs while extracting
aabidsofi19 Jan 31, 2025
ed8713d
add meshkit errors
aabidsofi19 Jan 31, 2025
7accbaf
add identification trace and more errors
aabidsofi19 Jan 31, 2025
59418e7
more test scenarios
aabidsofi19 Jan 31, 2025
761003a
go mod tidy
aabidsofi19 Jan 31, 2025
60e44ee
Update files/errors.go
aabidsofi19 Feb 1, 2025
d993d69
fallback to unstructured parsing for unknown schemes eg crds
aabidsofi19 Feb 1, 2025
9f13680
add more tests
aabidsofi19 Feb 1, 2025
d32f5a3
add conversion logic
aabidsofi19 Feb 1, 2025
44a2194
update docker parsing logic
aabidsofi19 Feb 1, 2025
4bd385d
chore: use schemas
aabidsofi19 Feb 2, 2025
f1f418c
add unsupported conversion err
aabidsofi19 Feb 2, 2025
f89ca11
add support for design oci images
aabidsofi19 Feb 2, 2025
92d0e6b
remove old detection logic
aabidsofi19 Feb 2, 2025
61bbda9
add extensions
aabidsofi19 Feb 2, 2025
0981a12
fix design oci parsing
aabidsofi19 Feb 3, 2025
6092233
remove comments
aabidsofi19 Feb 3, 2025
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
34 changes: 34 additions & 0 deletions files/conversion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package files

import (
"fmt"

// dockerTypes "github.com/docker/cli/cli/compose/types"

"github.com/layer5io/meshkit/utils/helm"
// "github.com/layer5io/meshkit/utils/kubernetes/kompose"
// "gopkg.in/yaml.v3"
"helm.sh/helm/v3/pkg/chart"
)

func ConvertHelmChartToKubernetesManifest(file IdentifiedFile) (string, error) {
chart, ok := file.ParsedFile.(*chart.Chart)
if chart != nil && !ok {
return "", fmt.Errorf("Failed to get *chart.Chart from identified file")
}
// empty kubernetes version because helm should figure it out
manifest, err := helm.DryRunHelmChart(chart, "")
if err != nil {
return "", err
}
return string(manifest), nil
}

func ConvertDockerComposeToKubernetesManifest(file IdentifiedFile) (string, error) {
parsedCompose, ok := file.ParsedFile.(ParsedCompose)
if !ok {
return "", fmt.Errorf("Failed to get *chart.Chart from identified file")
}

return parsedCompose.manifest, nil
}
344 changes: 344 additions & 0 deletions files/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,344 @@
package files

import (
"fmt"
"maps"
"slices"
"strings"

"github.com/layer5io/meshkit/errors"
)

var (
// Error code
ErrUnsupportedExtensionCode = "replace_me"
ErrUnsupportedExtensionForOperationCode = "replace_me"
ErrFailedToIdentifyFileCode = "replace_me"
ErrSanitizingFileCode = "replace_me"
ErrInvalidYamlCode = "replace_me"
ErrInvalidJsonCode = "replace_me"
ErrFailedToExtractTarCode = "replace_me"
ErrUnsupportedFileTypeCode = "replace_me"
ErrInvalidKubernetesManifestCode = "replace_me"
ErrInvalidMesheryDesignCode = "replace_me"
ErrInvalidHelmChartCode = "replace_me"
ErrInvalidDockerComposeCode = "replace_me"
ErrInvalidKustomizationCode = "replace_me"
ErrFileTypeNotSupportedForDesignConversion = "replace_me"
)

func ErrUnsupportedExtensionForOperation(operation string, fileName string, fileExt string, supportedExtensions []string) error {
sdescription := []string{
fmt.Sprintf("The file '%s' has an unsupported extension '%s' for the operation '%s'.", fileName, fileExt, operation),
fmt.Sprintf("Supported extensions for this operation are: %s.", strings.Join(supportedExtensions, ", ")),
}

ldescription := []string{
fmt.Sprintf("The file '%s' could not be used for the operation '%s' because the extension '%s' is not supported.", fileName, operation, fileExt),
fmt.Sprintf("The system is designed to handle files with the following extensions for this operation: %s.", strings.Join(supportedExtensions, ", ")),
}

probableCause := []string{
"The file extension does not match any of the supported formats for this operation.",
"The file may have been saved with an incorrect or unsupported extension.",
"The operation may have specific requirements for file types that are not met by this file.",
}

remedy := []string{
"Ensure the file is saved with one of the supported extensions for this operation.",
"Convert the file to a supported format before attempting the operation.",
"Check the documentation or requirements for the operation to verify the supported file types.",
}

return errors.New(ErrUnsupportedExtensionForOperationCode, errors.Critical, sdescription, ldescription, probableCause, remedy)
}

func ErrUnsupportedExtension(fileName string, fileExt string, supportedExtensionsMap map[string]bool) error {
supportedExtensions := slices.Collect(maps.Keys(supportedExtensionsMap))

sdescription := []string{
fmt.Sprintf("The file '%s' has an unsupported extension: '%s'.", fileName, fileExt),
fmt.Sprintf("Supported file extensions are: %s.", strings.Join(supportedExtensions, ", ")),
}

ldescription := []string{
fmt.Sprintf("The file '%s' could not be processed because the extension '%s' is not supported by the system.", fileName, fileExt),
fmt.Sprintf("The system is designed to handle files with the following extensions: %s.", strings.Join(supportedExtensions, ", ")),
}

probableCause := []string{
"The file extension does not match any of the supported formats.",
"The file may have been saved with an incorrect or unsupported extension.",
}

remedy := []string{
"Ensure the file is saved with one of the supported extensions.",
"Convert the file to a supported format before attempting to process it.",
}

return errors.New(ErrSanitizingFileCode, errors.Critical, sdescription, ldescription, probableCause, remedy)
}

func ErrInvalidYaml(fileName string, err error) error {
sdescription := []string{
fmt.Sprintf("The file '%s' contains invalid YAML syntax.", fileName),
}

ldescription := []string{
fmt.Sprintf("The file '%s' could not be parsed due to invalid YAML syntax.", fileName),
fmt.Sprintf("Error details: %s", err.Error()),
}

probableCause := []string{
"The YAML file may contain syntax errors, such as incorrect indentation, missing colons, or invalid characters.",
"The file may have been corrupted or improperly edited.",
}

remedy := []string{
"Review the YAML syntax in the file and correct any errors.",
"Use a YAML validator or linter to identify and fix issues.",
"Ensure the file adheres to the YAML specification.",
}

return errors.New(ErrInvalidYamlCode, errors.Critical, sdescription, ldescription, probableCause, remedy)
}

func ErrInvalidJson(fileName string, err error) error {

Choose a reason for hiding this comment

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

Highly detailed. Error codes specific to the file content type. 👏

sdescription := []string{
fmt.Sprintf("The file '%s' contains invalid JSON syntax.", fileName),
}

ldescription := []string{
fmt.Sprintf("The file '%s' could not be parsed due to invalid JSON syntax.", fileName),
fmt.Sprintf("Error details: %s", err.Error()),
}

probableCause := []string{
"The JSON file may contain syntax errors, such as missing commas, curly braces, or square brackets.",
"The file may have been corrupted or improperly edited.",
"Special characters or escape sequences may not have been properly formatted.",
}

remedy := []string{
"Review the JSON syntax in the file and correct any errors.",
"Use a JSON validator or linter to identify and fix issues.",
"Ensure the file adheres to the JSON specification (e.g., double quotes for keys and strings).",
"Check for common issues like trailing commas or unescaped special characters.",
}

return errors.New(ErrInvalidJsonCode, errors.Critical, sdescription, ldescription, probableCause, remedy)
}

func ErrFailedToExtractArchive(fileName string, err error) error {
sdescription := []string{
fmt.Sprintf("Failed to extract the archive '%s'.", fileName),
}

ldescription := []string{
fmt.Sprintf("An error occurred while attempting to extract the TAR archive '%s'.", fileName),
fmt.Sprintf("Error details: %s", err.Error()),
}

probableCause := []string{
"The archive may be corrupted or incomplete.",
"The archive may contain unsupported compression formats or features.",
"Insufficient permissions to read or write files during extraction.",
"The archive may have been created with a different tool or version that is incompatible.",
}

remedy := []string{
"Verify that the archive is not corrupted by checking its integrity or re-downloading it.",
"Ensure the archive uses a supported compression format (e.g., .zip, .tar, .tar.gz, ).",
"Check that you have sufficient permissions to read the archive and write to the destination directory.",
"Try using a different extraction tool or library to rule out compatibility issues.",
}

return errors.New(ErrFailedToExtractTarCode, errors.Critical, sdescription, ldescription, probableCause, remedy)
}

func ErrFailedToIdentifyFile(fileName string, fileExt string, identificationTrace map[string]error) error {

validTypes := slices.Collect(maps.Keys(identificationTrace))

sdescription := []string{
"The file '%s' could not be identified as a supported type",
}

// Build a detailed trace of identification attempts
var traceDetails []string
for fileType, err := range identificationTrace {
traceDetails = append(traceDetails, fmt.Sprintf("- Attempted to identify as '%s': %v", fileType, err))
}

ldescription := []string{
fmt.Sprintf("The file '%s' was not recognized as any of the supported file types %v.", fileName, validTypes),
"Identification attempts and errors:",
}
ldescription = append(ldescription, traceDetails...)

probableCause := []string{
"The file extension does not match any of the supported types.",
"The file may be corrupted or incomplete.",
"The file may have been saved with an incorrect or unsupported format.",
"The file may not conform to the expected structure for any supported type.",
}

remedy := []string{
"Ensure the file is saved with one of the supported extensions.",
"Verify the integrity of the file and ensure it is not corrupted.",
"Check the file's content and structure to ensure it matches one of the supported types.",
"Convert the file to a supported format before attempting to process it.",
}
return errors.New(ErrUnsupportedFileTypeCode, errors.Critical, sdescription, ldescription, probableCause, remedy)
}

func ErrInvalidKubernetesManifest(fileName string, err error) error {
sdescription := []string{
fmt.Sprintf("The file '%s' is not a valid Kubernetes manifest.", fileName),
}

ldescription := []string{
fmt.Sprintf("The file '%s' could not be parsed as a Kubernetes manifest due to invalid syntax or structure.", fileName),
fmt.Sprintf("Error details: %s", err.Error()),
}

probableCause := []string{
"The file may contain invalid YAML syntax or incorrect indentation.",
"The file may not conform to the Kubernetes API schema.",
"The file may be missing required fields or contain unsupported fields.",
}

remedy := []string{
"Review the YAML syntax in the file and correct any errors.",
"Use a Kubernetes YAML validator or linter to identify and fix issues.",
"Ensure the file adheres to the Kubernetes API specification.",
}

return errors.New(ErrInvalidKubernetesManifestCode, errors.Critical, sdescription, ldescription, probableCause, remedy)
}

func ErrInvalidHelmChart(fileName string, err error) error {
sdescription := []string{
fmt.Sprintf("The file '%s' is not a valid Helm chart.", fileName),
}

ldescription := []string{
fmt.Sprintf("The file '%s' could not be parsed as a Helm chart due to invalid structure or missing files.", fileName),
fmt.Sprintf("Error details: %s", err.Error()),
}

probableCause := []string{
"The file may be missing required files such as 'Chart.yaml' or 'values.yaml'.",
"The file may be corrupted or incomplete.",
"The file may not conform to the Helm chart specification.",
}

remedy := []string{
"Ensure the file contains all required Helm chart files (e.g., Chart.yaml, values.yaml).",
"Verify the integrity of the Helm chart archive.",
"Check the Helm documentation for the correct chart structure.",
}

return errors.New(ErrInvalidHelmChartCode, errors.Critical, sdescription, ldescription, probableCause, remedy)
}

func ErrInvalidDockerCompose(fileName string, err error) error {
sdescription := []string{
fmt.Sprintf("The file '%s' is not a valid Docker Compose file.", fileName),
}

ldescription := []string{
fmt.Sprintf("The file '%s' could not be parsed as a Docker Compose file due to invalid syntax or structure.", fileName),
fmt.Sprintf("Error details: %s", err.Error()),
}

probableCause := []string{
"The file may contain invalid YAML syntax or incorrect indentation.",
"The file may not conform to the Docker Compose specification.",
"The file may be missing required fields or contain unsupported fields.",
}

remedy := []string{
"Review the YAML syntax in the file and correct any errors.",
"Use a Docker Compose validator or linter to identify and fix issues.",
"Ensure the file adheres to the Docker Compose specification.",
}

return errors.New(ErrInvalidDockerComposeCode, errors.Critical, sdescription, ldescription, probableCause, remedy)
}

func ErrInvalidKustomization(fileName string, err error) error {
sdescription := []string{
fmt.Sprintf("The file '%s' is not a valid Kustomization file.", fileName),
}

ldescription := []string{
fmt.Sprintf("The file '%s' could not be parsed as a Kustomization file due to invalid syntax or structure.", fileName),
fmt.Sprintf("Error details: %s", err.Error()),
}

probableCause := []string{
"The file may be missing required fields such as 'resources' or 'bases'.",
"The file may contain invalid YAML syntax or incorrect indentation.",
"The file may not conform to the Kustomize specification.",
}

remedy := []string{
"Review the YAML syntax in the file and correct any errors.",
"Ensure the file contains all required fields for Kustomization.",
"Check the Kustomize documentation for the correct file structure.",
}

return errors.New(ErrInvalidKustomizationCode, errors.Critical, sdescription, ldescription, probableCause, remedy)
}

func ErrUnsupportedFileTypeForConversionToDesign(fileName string, fileType string) error {
sdescription := []string{
fmt.Sprintf("The file '%s' of type '%s' is not supported for conversion to a design", fileName, fileType),
}

ldescription := []string{
fmt.Sprintf("The file '%s' of type '%s' cannot be converted to design. Supported formats are: meshery-design, helm-chart, k8s-manifest, docker-compose.", fileName, fileType),
}

probableCause := []string{
"File doesn't match any supported IAC files",
"Attempting to convert a file type not enabled for design conversion",
}

remedy := []string{
"Verify the file format matches one of the supported types",
"Convert the file to a supported format before processing",
}

return errors.New(ErrFileTypeNotSupportedForDesignConversion, errors.Critical, sdescription, ldescription, probableCause, remedy)
}

var (
ErrNoTarInsideOCICode = "replace_me"
ErrEmptyOCIImageCode = "replace_me"
ErrUnCompressOCIArtifactCode = "replace_me"
ErrWaklingLocalDirectoryCode = "replace_me"
ErrDecodePatternCode = "replace_me"
)

// OCI Parsing errors

func ErrNoTarInsideOCi() error {
return errors.New(ErrNoTarInsideOCICode, errors.Alert, []string{"No tar file found inside OCI image"}, []string{"Unable to locate the compressed file(.tar.gz) inside the OCI image."}, []string{"The OCI image does not contain a ziped file."}, []string{"Verify that the OCI image contains a ziped file."})
}
func ErrEmptyOCIImage(err error) error {
return errors.New(ErrEmptyOCIImageCode, errors.Alert, []string{}, []string{}, []string{}, []string{})
}

func ErrUnCompressOCIArtifact(err error) error {
return errors.New(ErrUnCompressOCIArtifactCode, errors.Alert, []string{"Failed to uncompress OCI artifact"}, []string{err.Error()}, []string{"unable to uncompress OCI artifact", "OCI artifact may be corrupted"}, []string{"check if the OCI artifact is valid and not corrupted"})
}

func ErrWaklingLocalDirectory(err error) error {
return errors.New(ErrWaklingLocalDirectoryCode, errors.Alert, []string{"Failed to walk local directory"}, []string{err.Error()}, []string{"unable to walk local directory", "local directory may be corrupted"}, []string{"check if the local directory is valid and not corrupted"})
}

func ErrDecodePattern(err error) error {
return errors.New(ErrDecodePatternCode, errors.Alert, []string{"Error failed to decode design data into go slice"}, []string{err.Error()}, []string{}, []string{})
}
Loading
Loading