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

Kcl/parse deps req from rulesfile #22

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
132 changes: 126 additions & 6 deletions cmd/registry/push/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ import (
"os"
"path/filepath"

"github.com/blang/semver/v4"
"github.com/pterm/pterm"
"github.com/spf13/cobra"
"gopkg.in/yaml.v3"

"github.com/falcosecurity/falcoctl/internal/utils"
"github.com/falcosecurity/falcoctl/pkg/oci"
Expand Down Expand Up @@ -148,12 +151,22 @@ func (o *pushOptions) runPush(ctx context.Context, args []string) error {
}
}()

config := &oci.ArtifactConfig{
Name: o.Name,
Version: o.Version,
}

for i, p := range paths {
if err = utils.IsTarGz(filepath.Clean(p)); err != nil && !errors.Is(err, utils.ErrNotTarGz) {
return err
} else if err == nil {
continue
} else {
if o.ArtifactType == oci.Rulesfile {
if config, err = rulesConfigLayer(o.Printer.Logger, p, o.Artifact); err != nil {
return err
}
}
path, err := utils.CreateTarGzArchive(p)
if err != nil {
return err
Expand All @@ -165,11 +178,6 @@ func (o *pushOptions) runPush(ctx context.Context, args []string) error {
}
}

// Setup OCI artifact configuration
config := oci.ArtifactConfig{
Name: o.Name,
Version: o.Version,
}
if config.Name == "" {
// extract artifact name from ref, if not provided by the user
if config.Name, err = utils.NameFromRef(ref); err != nil {
Expand All @@ -186,7 +194,7 @@ func (o *pushOptions) runPush(ctx context.Context, args []string) error {
opts := ocipusher.Options{
ocipusher.WithTags(o.Tags...),
ocipusher.WithAnnotationSource(o.AnnotationSource),
ocipusher.WithArtifactConfig(config),
ocipusher.WithArtifactConfig(*config),
}

switch o.ArtifactType {
Expand All @@ -207,3 +215,115 @@ func (o *pushOptions) runPush(ctx context.Context, args []string) error {

return nil
}

const (
depsKey = "required_plugin_versions"
// engineKey is the key in the rulesfiles.
engineKey = "required_engine_version"
// engineRequirementKey is used as name for the engine requirement in the config layer for the rulesfile artifacts.
engineRequirementKey = "engine_version_semver"
)

func rulesConfigLayer(logger *pterm.Logger, filePath string, artifactOptions *options.Artifact) (*oci.ArtifactConfig, error) {
var data []map[string]interface{}

// Setup OCI artifact configuration
config := oci.ArtifactConfig{
Name: artifactOptions.Name,
Version: artifactOptions.Version,
}

yamlFile, err := os.ReadFile(filepath.Clean(filePath))
if err != nil {
return nil, fmt.Errorf("unable to open rulesfile %s: %w", filePath, err)
}

if err := yaml.Unmarshal(yamlFile, &data); err != nil {
return nil, fmt.Errorf("unable to unmarshal rulesfile %s: %w", filePath, err)
}

// Parse the plugin dependency.
// Check if the user has provided any.
if len(artifactOptions.Dependencies) != 0 {
logger.Info("Dependencies provided by user")
if err = config.ParseDependencies(artifactOptions.Dependencies...); err != nil {
return nil, err
}
} else {
// If no user provided then try to parse them from the rulesfile.
var found bool
logger.Info("Parsing dependencies from: ", logger.Args("rulesfile", filePath))
var requiredPluginVersionsEntry interface{}
var ok bool
for _, entry := range data {
if requiredPluginVersionsEntry, ok = entry[depsKey]; !ok {
continue
}

var deps []oci.ArtifactDependency
byteData, err := yaml.Marshal(requiredPluginVersionsEntry)
if err != nil {
return nil, fmt.Errorf("unable to parse dependencies from rulesfile: %w", err)
}
err = yaml.Unmarshal(byteData, &deps)
if err != nil {
return nil, fmt.Errorf("unable to parse dependencies from rulesfile: %w", err)
}
logger.Info("Dependencies correctly parsed from rulesfile")
// Set the deps.
config.Dependencies = deps
found = true
break
}
if !found {
logger.Warn("No dependencies were provided by the user and none were found in the rulesfile.")
}
}

// Parse the requirements.
// Check if the user has provided any.
if len(artifactOptions.Requirements) != 0 {
logger.Info("Requirements provided by user")
if err = config.ParseRequirements(artifactOptions.Requirements...); err != nil {
return nil, err
}
} else {
var found bool
var engineVersion string
logger.Info("Parsing requirements from: ", logger.Args("rulesfile", filePath))
// If no user provided requirements then try to parse them from the rulesfile.
for _, entry := range data {
if requiredEngineVersionEntry, ok := entry[engineKey]; ok {
// Check if the version is an int. This is for backward compatibility. The engine version used to be an
// int but internally used by falco as a semver minor version.
// 15 -> 0.15.0
if engVersionInt, ok := requiredEngineVersionEntry.(int); ok {
engineVersion = fmt.Sprintf("0.%d.0", engVersionInt)
} else {
engineVersion, ok = requiredEngineVersionEntry.(string)
if !ok {
return nil, fmt.Errorf("%s must be an int or a string respecting the semver specification, got type %T", engineKey, requiredEngineVersionEntry)
}

// Check if it is in semver format.
if _, err := semver.Parse(engineVersion); err != nil {
return nil, fmt.Errorf("%s must be in semver format: %w", engineVersion, err)
}
}

// Set the requirements.
config.Requirements = []oci.ArtifactRequirement{{
Name: engineRequirementKey,
Version: engineVersion,
}}
found = true
break
}
}
if !found {
logger.Warn("No requirements were provided by the user and none were found in the rulesfile.")
}
}

return &config, nil
}
10 changes: 4 additions & 6 deletions cmd/registry/push/push_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,13 @@ import (
testutils "github.com/falcosecurity/falcoctl/pkg/test"
)

//nolint:unused // false positive
const (
rulesfiletgz = "../../../pkg/test/data/rules.tar.gz"
rulesfileyaml = "../../../pkg/test/data/rules.yaml"
plugintgz = "../../../pkg/test/data/plugin.tar.gz"
rulesfiletgz = "../../../pkg/test/data/rules.tar.gz"
rulesfileyaml = "../../../pkg/test/data/rulesWithoutReqAndDeps.yaml"
rulesFileWithDepsAndReq = "../../../pkg/test/data/rules.yaml"
plugintgz = "../../../pkg/test/data/plugin.tar.gz"
)

//nolint:unused // false positive
var (
registry string
ctx = context.Background()
Expand Down Expand Up @@ -102,7 +101,6 @@ var _ = AfterSuite(func() {
Expect(os.RemoveAll(configDir)).Should(Succeed())
})

//nolint:unused // false positive
func executeRoot(args []string) error {
rootCmd.SetArgs(args)
rootCmd.SetOut(output)
Expand Down
Loading
Loading