Skip to content

Commit

Permalink
new(cmd,internal/utils,pkg/driver): use correct engine.kind config …
Browse files Browse the repository at this point in the history
…key.

Moreover, added a new ReplaceTextInFile utils, and added tests for it and ReplaceLineInFile.

Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
  • Loading branch information
FedeDP committed Nov 24, 2023
1 parent 42c069e commit 9ac2942
Show file tree
Hide file tree
Showing 5 changed files with 308 additions and 15 deletions.
51 changes: 40 additions & 11 deletions cmd/driver/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@ package driverconfig

import (
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/pterm/pterm"
"github.com/spf13/cobra"
"golang.org/x/net/context"
"gopkg.in/yaml.v3"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes"
Expand All @@ -38,7 +42,7 @@ import (
)

const (
configMapDriverTypeKey = "driver_mode"
configMapEngineKindKey = "engine.kind"
)

type driverConfigOptions struct {
Expand Down Expand Up @@ -169,8 +173,38 @@ func (o *driverConfigOptions) RunDriverConfig(ctx context.Context, cmd *cobra.Co
return config.StoreDriver(&driverCfg, o.ConfigFile)
}

func checkFalcoRunsWithDrivers(engineKind string) error {
// Modify the data in the ConfigMap/Falco config file ONLY if engine.kind is set to a known driver type.
// This ensures that we modify the config only for Falcos running with drivers, and not plugins/gvisor.
// Scenario: user has multiple Falco pods deployed in its cluster, one running with driver,
// other running with plugins. We must only touch the one running with driver.
if _, err := drivertype.Parse(engineKind); err != nil {
return fmt.Errorf("engine.kind is not driver driven: %s", engineKind)
}
return nil
}

func replaceDriverTypeInFalcoConfig(hostRoot string, driverType drivertype.DriverType) error {
return utils.ReplaceLineInFile(hostRoot+"/etc/falco/falco.yaml", "driver_mode:", "driver_mode: "+driverType.String(), 1)
falcoCfgFile := filepath.Join(hostRoot, "etc", "falco", "falco.yaml")
type engineCfg struct {
Kind string `yaml:"kind"`
}
type falcoCfg struct {
Engine engineCfg `yaml:"engine"`
}
yamlFile, err := os.ReadFile(filepath.Clean(falcoCfgFile))
if err != nil {
return err
}
cfg := falcoCfg{}
if err = yaml.Unmarshal(yamlFile, &cfg); err != nil {
return err
}
if err = checkFalcoRunsWithDrivers(cfg.Engine.Kind); err != nil {
return err
}
const configKindKey = "kind: "
return utils.ReplaceTextInFile(falcoCfgFile, configKindKey+cfg.Engine.Kind, configKindKey+driverType.String(), 1)
}

func replaceDriverTypeInK8SConfigMap(ctx context.Context, namespace, kubeconfig string, driverType drivertype.DriverType) error {
Expand Down Expand Up @@ -200,7 +234,7 @@ func replaceDriverTypeInK8SConfigMap(ctx context.Context, namespace, kubeconfig
return err
}
if configMapList.Size() == 0 {
return fmt.Errorf(`no configmaps matching "app.kubernetes.io/instance: falco" label were found`)
return errors.New(`no configmaps matching "app.kubernetes.io/instance: falco" label were found`)
}

type patchDriverTypeValue struct {
Expand All @@ -210,22 +244,17 @@ func replaceDriverTypeInK8SConfigMap(ctx context.Context, namespace, kubeconfig
}
payload := []patchDriverTypeValue{{
Op: "replace",
Path: "/data/" + configMapDriverTypeKey,
Path: "/data/" + configMapEngineKindKey,
Value: driverType.String(),
}}
plBytes, _ := json.Marshal(payload)

for i := 0; i < configMapList.Size(); i++ {
configMap := configMapList.Items[i]
// Modify the data in the ConfigMap ONLY if driver_mode is NOT set to plugin
// TODO: we must be sure that we are modifying the configmap for a Falco
// that is running with drivers, and not plugins.
// Scenario: user has multiple Falco pods deployed in its cluster, one running with driver,
// other running with plugins. We must only touch the one running with driver.
if val, ok := configMap.Data[configMapDriverTypeKey]; !ok || val == "none" {
currEngineKind := configMap.Data[configMapEngineKindKey]
if err = checkFalcoRunsWithDrivers(currEngineKind); err != nil {
continue
}

// Patch the configMap
if _, err = cl.CoreV1().ConfigMaps(configMap.Namespace).Patch(
ctx, configMap.Name, types.JSONPatchType, plBytes, metav1.PatchOptions{}); err != nil {
Expand Down
24 changes: 22 additions & 2 deletions internal/utils/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,31 @@ import (
"strings"
)

// ReplaceTextInFile searches for occurrences of searchFor in the file pointed by filePath,
// and substitutes the matching string with the provided one.
// At most n substitutions are made.
// If n < 0, there is no limit on the number of replacements.
func ReplaceTextInFile(filePath, searchFor, newText string, n int) error {
return replaceInFile(filePath, searchFor, n, func(line string) string {
return strings.Replace(line, searchFor, newText, 1)
})
}

// ReplaceLineInFile searches for occurrences of searchFor in the file pointed by filePath,
// and substitutes the matching line with the provided one.
// and substitutes the whole matching line with the provided one.
// At most n substitutions are made.
// If n < 0, there is no limit on the number of replacements.
func ReplaceLineInFile(filePath, searchFor, newLine string, n int) error {
return replaceInFile(filePath, searchFor, n, func(_ string) string {
return newLine
})
}

func replaceInFile(filePath, searchFor string, n int, replacementCB func(string) string) error {
if n == 0 {
return nil
}

stat, err := os.Stat(filePath)
if err != nil {
return err
Expand All @@ -42,7 +62,7 @@ func ReplaceLineInFile(filePath, searchFor, newLine string, n int) error {
replaced := 0
for i, line := range lines {
if strings.Contains(line, searchFor) {
lines[i] = newLine
lines[i] = replacementCB(line)
replaced++
if replaced == n {
break
Expand Down
244 changes: 244 additions & 0 deletions internal/utils/file_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2023 The Falco 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 utils

import (
"os"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestReplaceTextInFile(t *testing.T) {
tests := []struct {
fileContent string
searchFor string
replacementText string
n int
expectedFileContent string
}{
{
fileContent: `
foo:
bar:
- test1: 0
- test2: 0
`,
searchFor: "test1: 0",
replacementText: "test1: 1",
n: 1,
expectedFileContent: `
foo:
bar:
- test1: 1
- test2: 0
`,
},
// text not found
{
fileContent: `
foo:
bar:
- test1: 0
- test2: 0
`,
searchFor: "test3: 0",
replacementText: "test3: 1",
n: 1,
expectedFileContent: `
foo:
bar:
- test1: 0
- test2: 0
`,
},
// N == 0 -> no replacements
{
fileContent: `
foo:
bar:
- test1: 0
- test2: 0
`,
searchFor: "test1: 0",
replacementText: "test1: 1",
n: 0,
expectedFileContent: `
foo:
bar:
- test1: 0
- test2: 0
`,
},
// multiple replacements
{
fileContent: `
foo:
bar:
- test1: 0
- test2: 0
- test3: 0
`,
searchFor: "test",
replacementText: "testFoo",
n: -1,
expectedFileContent: `
foo:
bar:
- testFoo1: 0
- testFoo2: 0
- testFoo3: 0
`,
},
}

file, err := os.Create("test.txt")
require.NoError(t, err)
t.Cleanup(func() {
_ = file.Close()
_ = os.Remove("test.txt")
})

for _, test := range tests {
// Truncate and seek at beginning
err = file.Truncate(0)
require.NoError(t, err)
_, err = file.Seek(0, 0)
require.NoError(t, err)

_, err = file.WriteString(test.fileContent)
require.NoError(t, err)

err = ReplaceTextInFile(file.Name(), test.searchFor, test.replacementText, test.n)
require.NoError(t, err)

content, err := os.ReadFile(file.Name())
require.NoError(t, err)

assert.Equal(t, test.expectedFileContent, string(content))
}
}

func TestReplaceLineInFile(t *testing.T) {
tests := []struct {
fileContent string
searchFor string
replacementLine string
n int
expectedFileContent string
}{
{
fileContent: `
foo:
bar:
- test1: 0
- test2: 0
`,
searchFor: "test1",
replacementLine: "test1: 1",
n: 1,
expectedFileContent: `
foo:
bar:
test1: 1
- test2: 0
`,
},
// text not found
{
fileContent: `
foo:
bar:
- test1: 0
- test2: 0
`,
searchFor: "test3",
replacementLine: "test3: 1",
n: 1,
expectedFileContent: `
foo:
bar:
- test1: 0
- test2: 0
`,
},
// N == 0 -> no replacements
{
fileContent: `
foo:
bar:
- test1: 0
- test2: 0
`,
searchFor: "test1",
replacementLine: "test1: 1",
n: 0,
expectedFileContent: `
foo:
bar:
- test1: 0
- test2: 0
`,
},
// multiple replacements
{
fileContent: `
foo:
bar:
- test1: 0
- test2: 0
- test3: 0
`,
searchFor: "test",
replacementLine: "testFoo",
n: -1,
expectedFileContent: `
foo:
bar:
testFoo
testFoo
testFoo
`,
},
}

file, err := os.Create("test.txt")
require.NoError(t, err)
t.Cleanup(func() {
_ = file.Close()
_ = os.Remove("test.txt")
})

for _, test := range tests {
// Truncate and seek at beginning
err = file.Truncate(0)
require.NoError(t, err)
_, err = file.Seek(0, 0)
require.NoError(t, err)

_, err = file.WriteString(test.fileContent)
require.NoError(t, err)

err = ReplaceLineInFile(file.Name(), test.searchFor, test.replacementLine, test.n)
require.NoError(t, err)

content, err := os.ReadFile(file.Name())
require.NoError(t, err)

assert.Equal(t, test.expectedFileContent, string(content))
}
}
2 changes: 1 addition & 1 deletion pkg/driver/type/bpf.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
)

// TypeBpf is the string for the bpf driver type.
const TypeBpf = "bpf"
const TypeBpf = "ebpf"

func init() {
driverTypes[TypeBpf] = &bpf{}
Expand Down
Loading

0 comments on commit 9ac2942

Please sign in to comment.