Skip to content

Commit

Permalink
Added output argument and a new test for optional arguments
Browse files Browse the repository at this point in the history
Signed-off-by: ctartici <[email protected]>
  • Loading branch information
ctartici committed Feb 13, 2025
1 parent c9c8706 commit 610ea23
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 137 deletions.
30 changes: 21 additions & 9 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ spec:
values:
pilot:
traceSampling: 0.1
version: v1.23.2
version: v1.24.3
```

Note that the only field that was added is the `spec.version` field. There are a few situations however where the APIs are different and require different approaches to achieve the same outcome.
Expand Down Expand Up @@ -293,7 +293,7 @@ spec:
pilot:
env:
PILOT_ENABLE_STATUS: "true"
version: v1.24.0
version: v1.24.3
namespace: istio-system
```

Expand All @@ -304,18 +304,30 @@ Note the quotes around the value of `spec.values.pilot.env.PILOT_ENABLE_STATUS`.
Sail Operator's Istio resource does not have a `spec.components` field. Instead, you can enable and disable components directly by setting `spec.values.<component>.enabled: true/false`. Other functionality exposed through `spec.components` like the k8s overlays is not currently available.

### Converter Script
This script is used to convert an Istio in-cluster operator configuration to a Sail Operator configuration. Upon execution, a new YAML file will be created in the same directory which ISTIO-OPERATOR-CONFIG-YAML is present. The new file will be named: sail-operator-config.yaml
This script is used to convert an Istio in-cluster operator configuration to a Sail Operator configuration. Upon execution, the script takes an input YAML file and istio version and generates a sail operator configuration file.

#### Usage
To run the configuration-converter.sh script, you need to provide three arguments:
To run the configuration-converter.sh script, you need to provide four arguments, two of which are mandatory. The script uses default values for the optional arguments:

1. Input file path (-i <input>): The path to your Istio operator configuration YAML file (mandatory).
2. Output file path (-o <output>): The path where the converted Sail configuration will be saved. If not provided, the script will save the output with -sail.yaml appended to the input file name.
3. Namespace (-n <namespace>): The Kubernetes namespace for the Istio deployment. Defaults to istio-system if not provided.
4. Version (-v <version>): The version of Istio to be used (mandatory).

```bash
./tools/configuration-converter.sh -i /path/to/input.yaml -o [optional-output.yaml] -n [optional-namespace] -v [version]
```

##### Sample command with default namespace and output:

```bash
./tools/configuration-converter.sh -i /home/user/istio_config.yaml -v v1.24.3
```

ISTIO_OPERATOR_CONFIG_YAML_WITH_PATH: Path to the Istio operator configuration YAML file.
ISTIO_NAMESPACE: The Kubernetes namespace for the Istio deployment.
ISTIO_VERSION: The version of Istio to be used.
##### Sample command with custom output and namespace:

##### Example
```bash
./tools/configuration-converter.sh tools/istio_config.yaml istio-system v1.24.3
./tools/configuration-converter.sh -i /home/user/input/istio_config.yaml -o /home/user/output/output.yaml -n custom-namespace -v v1.24.3
```

> [!WARNING]
Expand Down
159 changes: 88 additions & 71 deletions pkg/converter/converter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"testing"

"github.com/istio-ecosystem/sail-operator/pkg/test/project"
Expand All @@ -28,47 +29,39 @@ import (

var (
controlPlaneNamespace = "istio-system"
// Test converter with latest istio version
istioVersion = supportedversion.List[len(supportedversion.List)-1].Name
istioVersion = supportedversion.Default
istioFile = filepath.Join(project.RootDir, "tools", "istioConfig.yaml")
sailFile = filepath.Join(project.RootDir, "tools", "istioConfig-sail.yaml")
)

func TestSimpleYamlConversion(t *testing.T) {
g := NewWithT(t)

istioYamlText := `apiVersion: install.istio.io/v1alpha1
func TestConversion(t *testing.T) {
t.Cleanup(func() {
os.Remove(istioFile)
os.Remove(sailFile)
})
testcases := []struct {
name string
input string
expectedOutput string
}{
{
name: "simple",
input: `apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
name: default
spec:`

sailYamlText := `apiVersion: sailoperator.io/v1
spec:`,
expectedOutput: `apiVersion: sailoperator.io/v1
kind: Istio
metadata:
name: default
spec:
version: %s
namespace: %s`
sailYamlText = fmt.Sprintf(sailYamlText, istioVersion, controlPlaneNamespace)

istioYamlFileWithPath, err := saveYamlToFile(istioYamlText)
g.Expect(err).To(Succeed(), "failed to write YAML file")

g.Expect(executeConfigConverter(istioYamlFileWithPath, controlPlaneNamespace, istioVersion)).To(Succeed(),
"error in execution of ./configuration-converter.sh")
isConversionValidated, err := validateYamlContent(sailYamlText)
g.Expect(err).To(Succeed(), "Can not open file to compare")
g.Expect(isConversionValidated).To(BeTrue(), "Converted content is not as expected")

err = os.Remove(filepath.Join(project.RootDir, "tools", "istioConfig.yaml"))
g.Expect(err).To(Succeed(), "Unable to delete istioConfig.yaml")
err = os.Remove(filepath.Join(project.RootDir, "tools", "sail-operator-config.yaml"))
g.Expect(err).To(Succeed(), "Unable to delete sail-operator-config.yaml")
}

func TestComplexYamlConversion(t *testing.T) {
g := NewWithT(t)

istioYamlText := `apiVersion: install.istio.io/v1alpha1
namespace: %s`,
},
{
name: "complex",
input: `apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
name: default
Expand All @@ -87,9 +80,8 @@ spec:
enableCRDTemplates: true
pilot:
env:
PILOT_ENABLE_STATUS: true`

sailYamlText := `apiVersion: sailoperator.io/v1
PILOT_ENABLE_STATUS: true`,
expectedOutput: `apiVersion: sailoperator.io/v1
kind: Istio
metadata:
name: default
Expand All @@ -107,63 +99,88 @@ spec:
PILOT_ENABLE_STATUS: "true"
enabled: false
version: %s
namespace: %s`
sailYamlText = fmt.Sprintf(sailYamlText, istioVersion, controlPlaneNamespace)

istioYamlFileWithPath, err := saveYamlToFile(istioYamlText)
g.Expect(err).To(Succeed(), "failed to write YAML file")

g.Expect(executeConfigConverter(istioYamlFileWithPath, controlPlaneNamespace, istioVersion)).To(Succeed(),
"error in execution of ./configuration-converter.sh")
isConversionValidated, err := validateYamlContent(sailYamlText)
g.Expect(err).To(Succeed(), "Can not open file to compare")
g.Expect(isConversionValidated).To(BeTrue(), "Converted content is not as expected")

err = os.Remove(filepath.Join(project.RootDir, "tools", "istioConfig.yaml"))
g.Expect(err).To(Succeed(), "Unable to delete istioConfig.yaml")
err = os.Remove(filepath.Join(project.RootDir, "tools", "sail-operator-config.yaml"))
g.Expect(err).To(Succeed(), "Unable to delete sail-operator-config.yaml")
namespace: %s`,
},
{
name: "mandatory-arguments-only",
input: `apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
name: default
spec:`,
expectedOutput: `apiVersion: sailoperator.io/v1
kind: Istio
metadata:
name: default
spec:
version: %s
namespace: %s`,
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
g := NewWithT(t)
err := saveYamlToFile(tc.input, istioFile)
g.Expect(err).To(Succeed(), "failed to write YAML file")

if tc.name == "mandatory-arguments-only" {
g.Expect(executeConfigConverter(istioFile, "", "", istioVersion)).To(Succeed(), "error in execution of ./configuration-converter.sh")
} else {
g.Expect(executeConfigConverter(istioFile, sailFile, controlPlaneNamespace,
istioVersion)).To(Succeed(), "error in execution of ./configuration-converter.sh")
}
tc.expectedOutput = fmt.Sprintf(tc.expectedOutput, istioVersion, controlPlaneNamespace)
isConversionValidated, err := validateYamlContent(tc.expectedOutput, sailFile)
g.Expect(err).To(Succeed(), "Can not open file to compare")
g.Expect(isConversionValidated).To(BeTrue(), "Converted content is not as expected")
})
}
}

// saveYamlToFile writes the given YAML content to IstioConfig.yaml
func saveYamlToFile(yamlText string) (string, error) {
// Create file in the same directory with the converter
istioYamlFile := "istioConfig.yaml"
istioYamlFileWithPath := filepath.Join(project.RootDir, "tools", istioYamlFile)
// saveYamlToFile writes the given YAML content to given file
func saveYamlToFile(input, istioFile string) error {
if err := os.WriteFile(istioFile, []byte(input), 0o644); err != nil {
return fmt.Errorf("failed to write YAML file: %w", err)
}
return nil
}

// Write to file
if err := os.WriteFile(istioYamlFileWithPath, []byte(yamlText), 0o644); err != nil {
return "", fmt.Errorf("failed to write YAML file: %w", err)
func executeConfigConverter(input, output, controlPlaneNamespace, istioVersion string) error {
converter := filepath.Join(project.RootDir, "tools", "configuration-converter.sh")
args := []string{
converter,
"-i", input,
"-v", istioVersion,
}

return istioYamlFileWithPath, nil
}
if output != "" {
args = append(args, "-o", output)
}

func executeConfigConverter(istioYamlFilePath, istioVersion, controlPlaneNamespace string) error {
// Define file path
configConverterPath := filepath.Join(project.RootDir, "tools", "configuration-converter.sh")
if controlPlaneNamespace != "" {
args = append(args, "-n", controlPlaneNamespace)
}

_, err := shell.ExecuteBashScript(configConverterPath, istioYamlFilePath, controlPlaneNamespace, istioVersion)
cmd := strings.Join(args, " ")
_, err := shell.ExecuteCommand(cmd)
if err != nil {
return fmt.Errorf("error in execution of %s %s %s %s: %w", configConverterPath, istioYamlFilePath, controlPlaneNamespace, istioVersion, err)
return fmt.Errorf("error in execution of %s -i %s -o %s -n %s -v %s: %w", converter, input, output, controlPlaneNamespace, istioVersion, err)
}
return nil
}

// compareYamlContent checks if the provided YAML string matches the content of converted config
func validateYamlContent(yamlString string) (bool, error) {
sailYamlWithPath := filepath.Join(project.RootDir, "tools", "sail-operator-config.yaml")

// compareYamlContent checks if the provided YAML content matches the content of converted sail operator config
func validateYamlContent(expectedOutput, sailFile string) (bool, error) {
// Write the input YAML string to a temporary file
tmpFile := filepath.Join(project.RootDir, "tools", "temp.yaml")
err := os.WriteFile(tmpFile, []byte(yamlString), 0o644)
err := os.WriteFile(tmpFile, []byte(expectedOutput), 0o644)
if err != nil {
return false, fmt.Errorf("failed to write temporary YAML file: %w", err)
}
defer os.Remove(tmpFile)

// The command will check if the files are equal, ignoring order
cmd := fmt.Sprintf("diff <(yq -P 'sort_keys(..)' -o=props %s) <(yq -P 'sort_keys(..)' -o=props %s)", sailYamlWithPath, tmpFile)
cmd := fmt.Sprintf("diff <(yq -P 'sort_keys(..)' -o=props %s) <(yq -P 'sort_keys(..)' -o=props %s)", sailFile, tmpFile)
output, err := shell.ExecuteCommand(cmd)
if err != nil {
return false, fmt.Errorf("error executing yq comparison: %w", err)
Expand Down
32 changes: 0 additions & 32 deletions tests/e2e/util/shell/shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
)

Expand Down Expand Up @@ -54,34 +53,3 @@ func ExecuteShell(command string, input string) (string, error) {

return stdout.String(), nil
}

// Execute bash script with optional arguments
func ExecuteBashScript(scriptPath string, args ...string) (string, error) {
absScriptPath, err := filepath.Abs(scriptPath)
if err != nil {
return "", fmt.Errorf("error getting absolute path: %w", err)
}

if _, err := os.Stat(absScriptPath); os.IsNotExist(err) {
return "", fmt.Errorf("script file does not exist: %s", absScriptPath)
}

scriptDir := filepath.Dir(absScriptPath)
cmdArgs := append([]string{absScriptPath}, args...)
cmd := exec.Command("bash", cmdArgs...)

var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr

// set dir to dir of the script before execution
cmd.Dir = scriptDir

// Run the script
err = cmd.Run()
if err != nil {
return "", fmt.Errorf("error executing script: %s, stderr: %s", err, stderr.String())
}

return stdout.String(), nil
}
Loading

0 comments on commit 610ea23

Please sign in to comment.