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

chore: add KeptnApp migration script #2959

Merged
merged 14 commits into from
Feb 6, 2024
14 changes: 14 additions & 0 deletions docs/docs/migrate/keptnapp/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,20 @@ introduced in the `v1beta1` API version:
3. Add the `app.kubernetes.io/managed-by: keptn` annotation
to the `KeptnApp` resource if it is not already there.

You can migrate your KeptnApp manually or, if you have go installed
RealAnna marked this conversation as resolved.
Show resolved Hide resolved
on your machine, use the script provided
[here](https://github.com/keptn/lifecycle-toolkit/tree/main/lifecycle-operator/converter).

```bash
go run convert_app.go path_to_keptnapp_to_convert path_to_desired_output_file
```

For instance, to run the example file conversion, you can do:
RealAnna marked this conversation as resolved.
Show resolved Hide resolved

```bash
go run convert_app.go example_keptnapp.yaml example_output.yaml
```

> **Note**
Be sure that all of your application resources
(such as
Expand Down
92 changes: 92 additions & 0 deletions lifecycle-operator/converter/convert_app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package main

import (
"fmt"
"log"
"os"

klcv1alpha3 "github.com/keptn/lifecycle-toolkit/lifecycle-operator/apis/lifecycle/v1alpha3"
klcv1beta1 "github.com/keptn/lifecycle-toolkit/lifecycle-operator/apis/lifecycle/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/yaml"
)

const keptnAnnotation = "app.kubernetes.io/managed-by"
const keptn = "keptn"

func main() {
if len(os.Args) != 3 {
fmt.Println("Usage: go run main.go app_cr_input_file.yaml output_file.yaml")
os.Exit(1)
}

inputFile := os.Args[1]
outputFile := os.Args[2]

inputContent, err := os.ReadFile(inputFile)
if err != nil {
log.Fatalf("Error reading input file: %v", err)
}

var keptnApp klcv1alpha3.KeptnApp
if err := yaml.Unmarshal(inputContent, &keptnApp); err != nil {
log.Fatalf("Error unmarshalling YAML: %v", err)
}

var keptnAppV1beta1 klcv1beta1.KeptnApp
if err := yaml.Unmarshal(inputContent, &keptnAppV1beta1); err != nil {
log.Fatalf("Error unmarshalling YAML: %v", err)
}

addKeptnAnnotation(&keptnAppV1beta1.ObjectMeta)
keptnAppV1beta1.TypeMeta.APIVersion = "lifecycle.keptn.sh/v1beta1"

// Transform KeptnAppContext
keptnAppContext := klcv1beta1.KeptnAppContext{
TypeMeta: metav1.TypeMeta{
Kind: "KeptnAppContext",
APIVersion: "lifecycle.keptn.sh/v1beta1",
},

ObjectMeta: metav1.ObjectMeta{
Name: keptnApp.Name,
Namespace: keptnApp.Namespace,
},
Spec: klcv1beta1.KeptnAppContextSpec{
DeploymentTaskSpec: klcv1beta1.DeploymentTaskSpec{

PreDeploymentTasks: keptnApp.Spec.PreDeploymentTasks,
PreDeploymentEvaluations: keptnApp.Spec.PreDeploymentEvaluations,
PostDeploymentTasks: keptnApp.Spec.PostDeploymentTasks,
PostDeploymentEvaluations: keptnApp.Spec.PostDeploymentEvaluations,
},
},
}

// Convert to YAML and write to output file
keptnAppV1beta1YAML, err := yaml.Marshal(keptnAppV1beta1)
if err != nil {
log.Fatalf("Error marshalling to YAML: %v", err)
}

keptnAppContextYAML, err := yaml.Marshal(keptnAppContext)
if err != nil {
log.Fatalf("Error marshalling to YAML: %v", err)
}

// Combine and write to output file
outputContent := fmt.Sprintf("%s\n---\n%s", string(keptnAppV1beta1YAML), string(keptnAppContextYAML))
if err := os.WriteFile(outputFile, []byte(outputContent), 0644); err != nil {
log.Fatalf("Error writing to output file: %v", err)
}

fmt.Println("Transformation completed. Output written to", outputFile)
}

func addKeptnAnnotation(resource *metav1.ObjectMeta) {
annotations := resource.GetAnnotations()
if annotations == nil {
resource.Annotations = make(map[string]string, 1)
}
resource.Annotations[keptnAnnotation] = keptn
}
76 changes: 76 additions & 0 deletions lifecycle-operator/converter/convert_app_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package main

import (
"os"
"testing"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const inputFileName = "example_keptnapp.yaml"
const outputFileName = "example_output.yaml"

func TestMigration(t *testing.T) {
// Set up a temporary directory for test files
// Run the main function with test arguments
inputFile := inputFileName
outputFile := outputFileName
os.Args = []string{"main", inputFile, outputFile}
main()

// Read the expected output file content
expectedOutput, err := os.ReadFile(outputFile)
if err != nil {
t.Fatalf("Error reading expected output file: %v", err)
}

// Read the actual output file content
actualOutput, err := os.ReadFile(outputFile)
if err != nil {
t.Fatalf("Error reading actual output file: %v", err)
}

// Assert that the actual output file content matches the expected output
if string(actualOutput) != string(expectedOutput) {
t.Errorf("Unexpected output content. Expected:\n%s\n\nActual:\n%s", string(expectedOutput), string(actualOutput))
}
}

func TestAddKeptnAnnotation(t *testing.T) {
// Test case 1: Annotations map is nil
t.Run("AnnotationsMapIsNil", func(t *testing.T) {
resource := &metav1.ObjectMeta{}
addKeptnAnnotation(resource)

// Check if the annotation was added
if value, exists := resource.Annotations[keptnAnnotation]; !exists || value != keptn {
t.Errorf("Annotation not added correctly. Expected: %s=%s, Actual: %s=%s", keptnAnnotation, keptn, keptnAnnotation, value)
}
})

// Test case 2: Annotations map is not nil
t.Run("AnnotationsMapIsNotNil", func(t *testing.T) {
// Existing annotations
existingAnnotations := map[string]string{
"existing-key": "existing-value",
}

resource := &metav1.ObjectMeta{
Annotations: existingAnnotations,
}

addKeptnAnnotation(resource)

// Check if the annotation was added
if value, exists := resource.Annotations[keptnAnnotation]; !exists || value != keptn {
t.Errorf("Annotation not added correctly. Expected: %s=%s, Actual: %s=%s", keptnAnnotation, keptn, keptnAnnotation, value)
}

// Check if existing annotations are preserved
for key, value := range existingAnnotations {
if resource.Annotations[key] != value {
t.Errorf("Existing annotation %s=%s is not preserved", key, value)
}
}
})
}
18 changes: 18 additions & 0 deletions lifecycle-operator/converter/example_keptnapp.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
apiVersion: lifecycle.keptn.sh/v1alpha3
kind: KeptnApp
metadata:
name: "some-keptn-app"
namespace: "my-app-ns"
spec:
version: "1.2.3"
workloads:
- name: podtato-head-left-arm
version: 0.2.7
preDeploymentTasks:
- pre-deployment-task
preDeploymentEvaluations:
- pre-deployment-evaluation
postDeploymentTasks:
- post-deployment-task
postDeploymentEvaluations:
- post-deployment-evaluation
32 changes: 32 additions & 0 deletions lifecycle-operator/converter/example_output.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
apiVersion: lifecycle.keptn.sh/v1beta1
kind: KeptnApp
metadata:
annotations:
app.kubernetes.io/managed-by: keptn
creationTimestamp: null
name: some-keptn-app
namespace: my-app-ns
spec:
version: 1.2.3
workloads:
- name: podtato-head-left-arm
version: 0.2.7
status: {}

---
apiVersion: lifecycle.keptn.sh/v1beta1
kind: KeptnAppContext
metadata:
creationTimestamp: null
name: some-keptn-app
namespace: my-app-ns
spec:
postDeploymentEvaluations:
- post-deployment-evaluation
postDeploymentTasks:
- post-deployment-task
preDeploymentEvaluations:
- pre-deployment-evaluation
preDeploymentTasks:
- pre-deployment-task
status: {}
Loading